Browse Source

Speed up declaration bundling for JS framework packages (#1618)

pull/1572/head
Paweł Kuna 7 days ago
committed by GitHub
parent
commit
4a4054d840
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 63
      .build/build-icons.mjs
  2. 27
      .build/rollup-plugins.mjs
  3. 1
      .gitignore
  4. 1
      packages/icons-astro/build.mjs
  5. 4
      packages/icons-astro/rollup.config.mjs
  6. 1
      packages/icons-preact/build.mjs
  7. 4
      packages/icons-preact/rollup.config.mjs
  8. 1
      packages/icons-react-native/build.mjs
  9. 28
      packages/icons-react-native/rollup.config.mjs
  10. 1
      packages/icons-react/build.mjs
  11. 4
      packages/icons-react/rollup.config.mjs
  12. 1
      packages/icons-vue/build.mjs
  13. 3
      packages/icons-vue/rollup.config.mjs

63
.build/build-icons.mjs

@ -6,12 +6,23 @@ import { stringify } from 'svgson';
/**
* Build icons
*
* @param name
* @param componentTemplate
* @param indexIconTemplate
* @param indexTypeTemplate
* @param extension
* @param pretty
* @param name package directory name inside `packages/`, e.g. `icons-react`
* @param componentTemplate returns the source of a single icon module; called
* with `{ type, name, namePascal, children, stringify, svg }`
* @param indexItemTemplate returns the line re-exporting an icon from the
* icons index; called with `{ type, name, namePascal, svg }`
* @param aliasTemplate returns the line exporting an alias in
* `src/aliases.ts`; called with `{ from, to, fromPascal, toPascal }`. When
* omitted, the aliases file is left empty
* @param extension extension of the generated icon modules
* @param key add a `key` attribute (`svg-0`, `svg-1`, …) to every icon node
* @param pascalCase rename `stroke-width` to `strokeWidth` in icon nodes
* @param pascalName name icon modules `Icon<PascalName>` instead of the
* kebab-case icon name
* @param indexFile file name of the icons index inside `src/icons`
* @param dtsEntry generate `src/icons-dts`, a single-module stand-in for the
* icon modules that the declaration bundle is built from (see `iconsDts`
* in rollup-plugins.mjs)
*/
export const buildJsIcons = ({
name,
@ -23,12 +34,14 @@ export const buildJsIcons = ({
pascalCase = false,
pascalName = true,
indexFile = 'icons.ts',
dtsEntry = false,
}) => {
const DIST_DIR = path.resolve(PACKAGES_DIR, name);
const aliases = getAliases(),
allIcons = getAllIcons(true, true);
let index = [];
let dtsIcons = [];
Object.entries(allIcons).forEach(([type, icons]) => {
icons.forEach((icon, i) => {
// process.stdout.write(
@ -72,6 +85,13 @@ export const buildJsIcons = ({
);
fs.writeFileSync(filePath, component, 'utf-8');
dtsIcons.push({
exportName: `Icon${iconNamePascal}`,
module: path.basename(filePath, `.${extension}`),
// the JSDoc block documenting the component, if the template has one
doc: component.match(/\/\*\*(?:(?!\*\/)[^])*\*\/(?=\s*const )/)?.[0],
});
index.push(
indexItemTemplate({
type,
@ -99,6 +119,37 @@ export const buildJsIcons = ({
}
fs.writeFileSync(path.resolve(DIST_DIR, `./src/aliases.ts`), aliasesStr || `export {};`, 'utf-8');
if (dtsEntry) {
buildDtsEntry({ dir: path.resolve(DIST_DIR, 'src/icons-dts'), icons: dtsIcons, aliases });
}
};
/**
* Every icon module has the same type, but emitting declarations for each of
* them separately dominates the bundle build time. Instead, write one module
* that re-declares all icons with the type inferred from a real icon module,
* and one that maps aliases onto it.
*/
const buildDtsEntry = ({ dir, icons, aliases }) => {
fs.ensureDirSync(dir);
const index = [`import Icon from '../icons/${icons[0].module}';`, ''];
icons.forEach(({ exportName, doc }) => {
if (doc) {
index.push(doc);
}
index.push(`export const ${exportName} = Icon;`);
});
fs.writeFileSync(path.resolve(dir, 'index.ts'), index.join('\n'), 'utf-8');
const aliasesStr = Object.entries(aliases || {})
.map(
([from, to]) =>
`export { Icon${toPascalCase(to)} as Icon${toPascalCase(from)} } from './index';\n`,
)
.join('');
fs.writeFileSync(path.resolve(dir, 'aliases.ts'), aliasesStr || `export {};`, 'utf-8');
};
export const buildIconsList = (name) => {

27
.build/rollup-plugins.mjs

@ -2,6 +2,33 @@ import { visualizer } from 'rollup-plugin-visualizer'
import license from 'rollup-plugin-license'
import esbuild from 'rollup-plugin-esbuild'
import { nodeResolve } from '@rollup/plugin-node-resolve';
import path from 'path';
/**
* For the declaration bundle only: resolve the icons index and the aliases to
* the generated `src/icons-dts` modules (see `buildDtsEntry` in
* build-icons.mjs), so rollup-plugin-dts emits declarations for one module
* instead of one per icon. Must be placed before `dts()`.
*/
export const iconsDts = (srcDir = 'src') => {
const src = path.resolve(srcDir);
const redirects = {
[path.join(src, 'icons')]: path.join(src, 'icons-dts/index.ts'),
[path.join(src, 'icons/index')]: path.join(src, 'icons-dts/index.ts'),
[path.join(src, 'aliases')]: path.join(src, 'icons-dts/aliases.ts'),
};
return {
name: 'icons-dts',
resolveId(source, importer) {
if (!importer || !source.startsWith('.')) {
return null;
}
return redirects[path.resolve(path.dirname(importer), source)] ?? null;
},
};
};
const getRollupPlugins = (pkg, minify) => {
return [

1
.gitignore

@ -25,6 +25,7 @@ _import.tsv
packages/icons*/icons/*
packages/icons*/src/icons/*
packages/icons*/src/icons-dts/
packages/icons*/src/icons.js
packages/icons*/src/icons.ts
packages/icons*/src/aliases.ts

1
packages/icons-astro/build.mjs

@ -30,6 +30,7 @@ const indexItemTemplate = ({
const aliasTemplate = ({ fromPascal, to }) => `export { default as Icon${fromPascal} } from './icons/${to}';\n`
buildJsIcons({
dtsEntry: true,
name: 'icons-astro',
componentTemplate,
indexItemTemplate,

4
packages/icons-astro/rollup.config.mjs

@ -1,5 +1,5 @@
import fs from 'fs'
import { getRollupConfig } from '../../.build/rollup-plugins.mjs'
import { getRollupConfig, iconsDts } from '../../.build/rollup-plugins.mjs'
import dts from 'rollup-plugin-dts'
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8'))
@ -37,7 +37,7 @@ export default [
format: 'es',
},
],
plugins: [dts()],
plugins: [iconsDts(), dts()],
},
...getRollupConfig(pkg, outputFileName, bundles, {}),
]

1
packages/icons-preact/build.mjs

@ -19,6 +19,7 @@ const indexItemTemplate = ({
const aliasTemplate = ({ fromPascal, toPascal }) => `export { default as Icon${fromPascal} } from './icons/Icon${toPascal}';\n`
buildJsIcons({
dtsEntry: true,
name: 'icons-preact',
componentTemplate,
indexItemTemplate,

4
packages/icons-preact/rollup.config.mjs

@ -1,5 +1,5 @@
import fs from 'fs'
import { getRollupConfig } from '../../.build/rollup-plugins.mjs'
import { getRollupConfig, iconsDts } from '../../.build/rollup-plugins.mjs'
import dts from "rollup-plugin-dts";
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8'))
@ -29,7 +29,7 @@ export default [
format: 'es',
},
],
plugins: [dts()],
plugins: [iconsDts(), dts()],
},
...getRollupConfig(pkg, outputFileName, bundles, {
preact: 'preact'

1
packages/icons-react-native/build.mjs

@ -19,6 +19,7 @@ const indexItemTemplate = ({
const aliasTemplate = ({ fromPascal, toPascal }) => `export { default as Icon${fromPascal} } from './icons/Icon${toPascal}';\n`
buildJsIcons({
dtsEntry: true,
name: 'icons-react-native',
componentTemplate,
indexItemTemplate,

28
packages/icons-react-native/rollup.config.mjs

@ -1,5 +1,5 @@
import fs from 'fs';
import { getRollupConfig } from '../../.build/rollup-plugins.mjs';
import { getRollupConfig, iconsDts } from '../../.build/rollup-plugins.mjs';
import dts from 'rollup-plugin-dts';
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
@ -30,7 +30,7 @@ export default [
format: 'es',
},
],
plugins: [dts()],
plugins: [iconsDts(), dts()],
},
{
input: './src/icons/index.ts',
@ -49,26 +49,14 @@ export default [
emitDeclarationOnly: true,
},
}),
],
external: ['react', 'react-native-svg'],
},
{
input: './src/icons/index.ts',
output: [
// The CJS build ships the same per-icon declarations — copy them
// instead of emitting every icon a second time.
{
dir: 'dist/cjs/icons',
format: 'es',
preserveModules: true,
preserveModulesRoot: 'src',
},
],
plugins: [
dts({
compilerOptions: {
declaration: true,
emitDeclarationOnly: true,
name: 'copy-icons-dts-to-cjs',
writeBundle() {
fs.cpSync('dist/icons', 'dist/cjs/icons', { recursive: true });
},
}),
},
],
external: ['react', 'react-native-svg'],
},

1
packages/icons-react/build.mjs

@ -41,6 +41,7 @@ const aliasTemplate = ({ fromPascal, toPascal }) =>
`export { default as Icon${fromPascal} } from './icons/Icon${toPascal}';\n`;
buildJsIcons({
dtsEntry: true,
name: 'icons-react',
componentTemplate,
indexItemTemplate,

4
packages/icons-react/rollup.config.mjs

@ -1,5 +1,5 @@
import fs from 'fs';
import { getRollupConfig } from '../../.build/rollup-plugins.mjs';
import { getRollupConfig, iconsDts } from '../../.build/rollup-plugins.mjs';
import dts from 'rollup-plugin-dts';
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
@ -36,7 +36,7 @@ export default [
format: 'es',
},
],
plugins: [dts()],
plugins: [iconsDts(), dts()],
},
...getRollupConfig(pkg, outputFileName, bundles, {

1
packages/icons-vue/build.mjs

@ -19,6 +19,7 @@ const indexItemTemplate = ({
const aliasTemplate = ({ fromPascal, toPascal }) => `export { default as Icon${fromPascal} } from './icons/Icon${toPascal}';\n`
buildJsIcons({
dtsEntry: true,
name: 'icons-vue',
componentTemplate,
indexItemTemplate,

3
packages/icons-vue/rollup.config.mjs

@ -1,5 +1,5 @@
import fs from 'fs'
import { getRollupConfig } from '../../.build/rollup-plugins.mjs'
import { getRollupConfig, iconsDts } from '../../.build/rollup-plugins.mjs'
import dts from "rollup-plugin-dts";
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8'))
@ -30,6 +30,7 @@ export default [
},
],
plugins: [
iconsDts(),
dts({
compilerOptions: {
preserveSymlinks: false

Loading…
Cancel
Save