From 4636db5204854d38e05397aeab3edadce3dccb46 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Tue, 21 Feb 2023 22:27:57 +0100 Subject: [PATCH] Task: Use different approach to build react-ui-components: build each file separately the advantage is we dont have so many empty chunks and less files to publish. Also previously with importing from the unstyled compontents would still include css as the chunking wasnt optimal. --- cssModules.js | 6 +- packages/react-ui-components/esbuild.js | 189 +++++++++--------- packages/react-ui-components/example/test.jsx | 10 +- 3 files changed, 106 insertions(+), 99 deletions(-) diff --git a/cssModules.js b/cssModules.js index 8ac6b74ceb..02cca325ca 100644 --- a/cssModules.js +++ b/cssModules.js @@ -55,7 +55,9 @@ const cssModules = (options) => { sourceMap: true, targets: options.targets, drafts: options.drafts, - visitor: options.visitor + visitor: options.visitor, + // this way the correct relative path for the source map will be generated ;) + projectRoot: join(initialOptions.absWorkingDir || process.cwd(), initialOptions.outdir) }); if (!exports) { @@ -64,7 +66,7 @@ const cssModules = (options) => { const id = "css-modules:\/\/" + createHash("sha256").update(path).digest('base64url') + '.css' - const finalcode = code.toString("utf8") + `/*# sourceMappingURL=data:text/plain;base64,${map.toString("base64")} */`; + const finalcode = code.toString("utf8") + `/*# sourceMappingURL=data:application/json;base64,${map.toString("base64")} */`; transpiledCssModulesMap.set( id, diff --git a/packages/react-ui-components/esbuild.js b/packages/react-ui-components/esbuild.js index 5613a5c010..8327ce4e32 100644 --- a/packages/react-ui-components/esbuild.js +++ b/packages/react-ui-components/esbuild.js @@ -3,106 +3,111 @@ const { compileWithCssVariables } = require('../../cssVariables') const nodePath = require("path") const { writeFile, mkdir } = require("fs/promises") -const packageJson = require("./package.json"); const { cssModules } = require('../../cssModules'); const { build } = require('esbuild'); -build({ - entryPoints: [ - "./src/index.ts", - "./src/unstyled.ts", - "./src/identifiers.ts", +const { readdirSync } = require("fs") - // we use each component as entry point to not bundle all code directly into the index.js - // so we have proper code splitting when importing only a single component from this lib - "./src/enhanceWithClickOutside", - "./src/Badge", - "./src/Bar", - "./src/Button", - "./src/ButtonGroup", - "./src/CheckBox", - "./src/DateInput", - "./src/Dialog", - "./src/DropDown", - "./src/Frame", - "./src/Headline", - "./src/Icon", - "./src/IconButton", - "./src/IconButtonDropDown", - "./src/Label", - "./src/Logo", - "./src/SelectBox", - "./src/SideBar", - "./src/Tabs", - "./src/TextArea", - "./src/TextInput", - "./src/ToggablePanel", - "./src/Tooltip", - "./src/Tree", - "./src/MultiSelectBox", - "./src/MultiSelectBox_ListPreviewSortable", - "./src/SelectBox_Option_SingleLine", - "./src/SelectBox_Option_MultiLineWithThumbnail" - ], - external: Object.keys({...packageJson.dependencies, ...packageJson.peerDependencies}), - outdir: "dist", - sourcemap: "linked", - logLevel: 'info', - target: 'es2020', - assetNames: './_css/[hash]', - chunkNames: './_chunk/[hash]', - color: true, - bundle: true, - splitting: true, - format: "esm", - loader: { - '.js': 'tsx', - '.svg': 'dataurl', - '.css': 'copy' - }, - metafile: true, - write: false, // we dont write directly see `.then()` below - plugins: [ - cssModules( - { - includeFilter: /\.css$/, - visitor: compileWithCssVariables(), - targets: { - chrome: 80 // aligns somewhat to es2020 - }, - drafts: { - nesting: true - }, - cssModulesPattern: `neos-[hash]_[local]` - } - ) - ] -}).then((result) => { - if (result.errors.length) { - return; +/** + * @param {String} dir + * @returns {Generator} + */ +function *walkSync(dir) { + const files = readdirSync(dir, { withFileTypes: true }); + for (const file of files) { + if (file.isDirectory()) { + yield* walkSync(nodePath.join(dir, file.name)); + } else { + yield nodePath.join(dir, file.name); + } } +} + +async function main() { - // check for incorrect build or if we have accidentally bundled dependencies what we dont want at all ;) - for (const path of Object.keys(result.metafile.inputs)) { - if (path.startsWith("src/") || path.startsWith("css-modules:")) { - continue; + /** + * we select all ts,js files that are not tests + * this logic should always align to the include and exclude patterns in `tsconfig.esmtypes.json` + */ + const entryPoints = [...walkSync(nodePath.join(__dirname, "src"))].filter((file) => { + if (/(\.spec\.[^/]+|\.story\.jsx?|\.d\.ts)$/.test(file)) { + return false; } - throw new Error(`File ${path} doesnt belong to the currently bundled package, yet is not listed as dependeny.`) - } + if (/(\.tsx?|\.jsx?)$/.test(file)) { + return true; + } + return false; + }) - // we regex replace unused chunk imports in the js files, - // this fixes the suboptimal output - // see issue https://github.com/evanw/esbuild/issues/2922 + console.time("Build") - const unusedImportsRegex = /^import ".*_chunk\/[^;]+;$/gm; - result.outputFiles.forEach(async (file) => { - await mkdir(nodePath.dirname(file.path), { recursive: true}) + const buildResultsPromise = entryPoints.map((entryPoint) => build({ + entryPoints: [entryPoint], + outbase: "src", + outdir: "dist", + sourcemap: "linked", + bundle: true, + logLevel: 'silent', + target: 'es2020', + assetNames: './_css/[hash]', + color: true, + format: "esm", + write: false, + loader: { + '.js': 'tsx', + '.svg': 'dataurl', + '.css': 'copy' + }, + plugins: [ + { + name: "bundel-only-ressources-for-each-entry", + setup: ({onResolve}) => { + onResolve({filter: /.*/}, ({path}) => { + if (path.endsWith(".css") || path.endsWith(".svg")) { + return; + } - if (file.path.endsWith(".js") && !file.path.endsWith(".map.js")) { - const replacedUnusedImports = file.text.replace(unusedImportsRegex, ""); - writeFile(file.path, replacedUnusedImports, { encoding: "utf8" }) - } else { - writeFile(file.path, file.contents) + if (path === entryPoint) { + return; + } + + return { + external: true + } + }) + } + }, + cssModules( + { + includeFilter: /\.css$/, + visitor: compileWithCssVariables(), + targets: { + chrome: 80 // aligns somewhat to es2020 + }, + drafts: { + nesting: true + }, + cssModulesPattern: `neos-[hash]_[local]` + } + ) + ] + })) + + const buildResults = await Promise.all(buildResultsPromise) + const writtenFiles = new Set(); + for (const buildResult of buildResults) { + for (const file of buildResult.outputFiles) { + if (writtenFiles.has(file.path)) { + continue; + } + writtenFiles.add(file.path); + await mkdir(nodePath.dirname(file.path), { recursive: true }) + writeFile(file.path, file.contents); } - }) -}) + } + + console.timeEnd("Build") + console.log(`Wrote ${writtenFiles.size} files.`); +} + +main(); diff --git a/packages/react-ui-components/example/test.jsx b/packages/react-ui-components/example/test.jsx index 3f1853aa72..ab1e685b7e 100644 --- a/packages/react-ui-components/example/test.jsx +++ b/packages/react-ui-components/example/test.jsx @@ -1,6 +1,6 @@ import React from "react"; import { render } from "react-dom"; -import { IconButton, Icon, Button, Headline, Label, ToggablePanel } from "../dist/index" +import { IconButton, Icon, Button, Headline, Logo, ToggablePanel } from "../dist/index" import './font.css'; // The components @@ -22,19 +22,19 @@ const App = () => ( gap: "2rem", width: "100px" }}> + Hello - - + Header - Contents + Marc Henry war hier ^^ - + )