Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
27 changes: 27 additions & 0 deletions .changeset/sixty-llamas-cheer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
'@talend/react-faceted-search-query-client': major
'@talend/scripts-config-react-webpack': major
'@talend/json-schema-form-core': major
'@talend/react-faceted-search': major
'@talend/storybook-docs': major
'@talend/design-system': major
'@talend/design-tokens': major
'@talend/react-flow-designer': major
'@talend/router-bridge': major
'@talend/react-bootstrap': major
'@talend/assets-api': major
'@talend/react-cmf-router': major
'@talend/react-components': major
'@talend/react-containers': major
'@talend/ui-playground': major
'@talend/react-cmf-cqrs': major
'@talend/react-dataviz': major
'@talend/react-stepper': major
'@talend/react-forms': major
'@talend/react-sagas': major
'@talend/bootstrap-theme': major
'@talend/http': major
'@talend/react-cmf': major
---

chore: move from sass to css
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"test:cron": "talend-yarn-workspace run test:cron",
"start-components": "yarn workspace @talend/react-components run start",
"start-containers": "yarn workspace @talend/react-containers run start",
"start-design-system": "yarn workspace @talend/design-system run start",
"start-stepper": "yarn workspace @talend/react-stepper run start",
"start-forms": "yarn workspace @talend/react-forms run start",
"start-theme": "yarn workspace @talend/bootstrap-theme run start",
Expand Down
10 changes: 9 additions & 1 deletion packages/components/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ The API we have for all components is the following for an event handler
function onClick(event, payload) {
//do what ever you want
}
return <APureComponents onClick={onClick} />
return <APureComponents onClick={onClick} />;
```

### Write tests + docs
Expand Down Expand Up @@ -164,6 +164,14 @@ The stories are registred this way:
- npm run lint -> check the code style
- npm run watch -> watch the source to trigger a build

## CSS module build helper

Run `node css.js` from this package root to mirror every `*.module.scss` to a sibling `*.module.css` and rewrite the imports that point to it.

- Scans the package for `*.module.scss` files (ignoring node_modules, lib, lib-esm, .turbo, .git).
- Compiles each of them with `sass` into a `*.module.css` that sits in the same folder.
- Updates `.js`, `.jsx`, `.ts`, and `.tsx` files so `.module.scss` imports point to the new `.module.css` files.

## LICENSE

Copyright (c) 2006-2016 Talend
Expand Down
184 changes: 184 additions & 0 deletions packages/components/css.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
#!/usr/bin/env node

/**
* Run `node css.js` from this package root to compile every `*.module.scss` to a sibling `*.module.css` and rewrite imports accordingly.
*
* - Scans the package for `*.module.scss` files (ignoring node_modules, lib, lib-esm, .turbo, .git).
* - Compiles each of them with `sass` into a `*.module.css` that sits in the same folder.
* - Updates `.js`, `.jsx`, `.ts`, and `.tsx` files so `.module.scss` imports point to the new `.module.css` files.
* - Deletes the original `*.module.scss` files after successful compilation.
*/
/* eslint-disable no-console */
const fs = require('fs');
const path = require('path');
const sass = require('sass');
const { pathToFileURL } = require('url');

const PACKAGE_ROOT = path.resolve(__dirname);
const NODE_MODULES_PATH = path.join(PACKAGE_ROOT, 'node_modules');
const WORKSPACE_NODE_MODULES = path.resolve(PACKAGE_ROOT, '..', '..', 'node_modules');
const IGNORED_DIRECTORIES = new Set(['node_modules', 'lib', 'lib-esm', '.turbo', '.git']);

function assertPackageRoot() {
const packageJson = path.join(PACKAGE_ROOT, 'package.json');
if (!fs.existsSync(packageJson)) {
throw new Error(
`No package.json found in ${PACKAGE_ROOT}. Run this script from the package root.`,
);
}
}

function toRelative(filePath) {
return path.relative(PACKAGE_ROOT, filePath) || '.';
}

function getPkgRoot(filename) {
let current = path.dirname(filename);
while (true) {
if (fs.existsSync(path.join(current, 'package.json'))) {
return `${current}/`;
}
const parent = path.dirname(current);
if (parent === current) {
throw new Error(`Unable to find package.json for ${filename}`);
}
current = parent;
}
}

function getInfo(importPath) {
const parts = importPath.split('/');
const isScoped = importPath.startsWith('@');
const packageName = isScoped ? `${parts[0]}/${parts[1]}` : parts[0];
const rest = isScoped ? parts.slice(2) : parts.slice(1);
const mainPath = require.resolve(packageName, { paths: [PACKAGE_ROOT] });
return {
base: getPkgRoot(mainPath),
url: rest.join('/'),
};
}

function createImporter() {
// https://sass-lang.com/documentation/js-api/interfaces/Options
return {
// Allow tilde-prefixed imports the same way webpack does.
findFileUrl(url) {
if (!url.startsWith('~')) {
return null; // fallback to default resolution via loadPaths
}
const info = getInfo(url.slice(1));
return new URL(info.url, pathToFileURL(info.base));
},
};
}

function buildSassOptions(sourceFile) {
const loadPaths = [path.dirname(sourceFile), PACKAGE_ROOT, NODE_MODULES_PATH];
if (fs.existsSync(WORKSPACE_NODE_MODULES)) {
loadPaths.push(WORKSPACE_NODE_MODULES);
}
return {
loadPaths,
sourceMap: false,
importers: [createImporter()],
};
}

function walk(startDir, matcher, acc = []) {
const entries = fs.readdirSync(startDir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(startDir, entry.name);
if (entry.isDirectory()) {
if (IGNORED_DIRECTORIES.has(entry.name)) {
continue;
}
walk(fullPath, matcher, acc);
continue;
}
if (entry.isFile() && matcher(entry.name, fullPath)) {
acc.push(fullPath);
}
}
return acc;
}

function findModuleScssFiles() {
return walk(PACKAGE_ROOT, name => name.endsWith('.module.scss'));
}

function findCodeFiles() {
const extensions = new Set(['.js', '.jsx', '.ts', '.tsx']);
return walk(PACKAGE_ROOT, name => extensions.has(path.extname(name)));
}

function compileModuleScss(filePath) {
try {
const targetPath = filePath.replace(/\.module\.scss$/, '.module.css');
const result = sass.compile(filePath, buildSassOptions(filePath));
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
fs.writeFileSync(targetPath, result.css);
console.log(`compiled ${toRelative(filePath)} -> ${toRelative(targetPath)}`);
return { source: filePath, target: targetPath };
} catch (e) {
console.error(`failed to compile ${toRelative(filePath)}: ${e.message}`);
return null;
}
}

function updateModuleImports(filePath) {
// Do not rewrite this script itself
if (path.resolve(filePath) === path.resolve(__filename)) {
return false;
}
const content = fs.readFileSync(filePath, 'utf8');
if (!content.includes('.module.scss')) {
return false;
}
const updated = content.replace(/\.module\.scss\b/g, '.module.css');
if (updated === content) {
return false;
}
fs.writeFileSync(filePath, updated);
console.log(`rewrote imports in ${toRelative(filePath)}`);
return true;
}

function main() {
assertPackageRoot();
const scssFiles = findModuleScssFiles();
if (scssFiles.length === 0) {
console.log('No *.module.scss files found.');
return;
}

console.log(`Found ${scssFiles.length} *.module.scss file(s).`);
const compiled = scssFiles.map(compileModuleScss).filter(Boolean);

const codeFiles = findCodeFiles();
let updatedImports = 0;
codeFiles.forEach(filePath => {
if (updateModuleImports(filePath)) {
updatedImports += 1;
}
});

// Delete SCSS sources that compiled successfully
let deletedCount = 0;
compiled.forEach(({ source, target }) => {
try {
if (fs.existsSync(target)) {
fs.unlinkSync(source);
deletedCount += 1;
console.log(`deleted ${toRelative(source)}`);
}
} catch (e) {
console.warn(`could not delete ${toRelative(source)}: ${e.message}`);
}
});

console.log(
`Done: generated ${compiled.length} CSS file(s), updated imports in ${updatedImports} file(s), deleted ${deletedCount} SCSS file(s).`,
);
}

main();
5 changes: 5 additions & 0 deletions packages/components/custom.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,8 @@ declare module '*.scss' {
const contents: Record<string, string>;
export default contents;
}

declare module '*.css' {
const contents: Record<string, string>;
export default contents;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Icon from '../Icon';
import Skeleton from '../Skeleton';
import I18N_DOMAIN_COMPONENTS from '../constants';
import getDefaultT from '../translate';
import theme from './AboutDialog.module.scss';
import theme from './AboutDialog.module.css';
import { AboutDialogTable, Text } from './AboutDialogTable.component';

function AboutDialog({
Expand Down
25 changes: 25 additions & 0 deletions packages/components/src/AboutDialog/AboutDialog.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* stylelint-disable color-hex-case */
.about-dialog :global(.modal-body) {
text-align: center;
padding-top: 0.625rem;
padding-left: 2rem;
padding-right: 2rem;
}
.about-dialog :global(.modal-body) .about-logo {
width: 4.125rem;
height: 4.125rem;
}
.about-dialog :global(.modal-body) .about-excerpt {
margin-top: 1.25rem;
}
.about-dialog :global(.modal-body) .about-versions {
margin: 0 auto;
margin-top: 1.875rem;
text-align: justify;
table-layout: fixed;
}
.about-dialog :global(.modal-body) .about-versions th,
.about-dialog :global(.modal-body) .about-versions td {
padding: 5px;
text-align: left;
}
36 changes: 0 additions & 36 deletions packages/components/src/AboutDialog/AboutDialog.module.scss

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import classNames from 'classnames';
import PropTypes from 'prop-types';
import Skeleton from '../Skeleton';
import { getI18nInstance } from '../translate';
import theme from './AboutDialog.module.scss';
import theme from './AboutDialog.module.css';

const i18n = getI18nInstance();

Expand Down
2 changes: 1 addition & 1 deletion packages/components/src/ActionBar/ActionBar.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next';
import { Action, Actions, ActionDropdown, ActionSplitDropdown } from '../Actions';
import Inject from '../Inject';
import I18N_DOMAIN_COMPONENTS from '../constants';
import css from './ActionBar.module.scss';
import css from './ActionBar.module.css';

const DISPLAY_MODES = {
DROPDOWN: 'dropdown',
Expand Down
Loading
Loading