Skip to content

Commit bb5b626

Browse files
committed
feat(tools): allow consumers to specify additional packages to bundle
1 parent 0e9a413 commit bb5b626

File tree

4 files changed

+81
-59
lines changed

4 files changed

+81
-59
lines changed

.changeset/calm-apes-tie.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@patternfly/pfe-tools": minor
3+
---
4+
5+
Add `additionalPackages` option to `singleFileBundle`

tools/pfe-tools/11ty/plugins/pfe-assets.cjs

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,42 +10,62 @@ function exists(x) {
1010
}
1111
}
1212

13-
/** Generate a map of files per package which should be copied to the site dir */
14-
function getFilesToCopy() {
13+
/**
14+
* Generate a map of files per package which should be copied to the site dir
15+
* @param {object} [options]
16+
* @param {string} [options.prefix='pfe'] element prefix e.g. 'pfe' for 'pfe-button'
17+
*/
18+
function getFilesToCopy(options) {
1519
/** best guess at abs-path to repo root */
1620
const repoRoot = path.join(__dirname, '..', '..', '..', '..').replace(/node_modules\/?$/g, '');
1721

22+
const prefix = `${(options?.prefix ?? 'pfe').replace(/-$/, '')}-`;
23+
24+
const hasElements = exists(path.join(repoRoot, 'elements'));
25+
const hasCore = exists(path.join(repoRoot, 'core'));
26+
27+
if (!hasElements && !hasCore) {
28+
return null;
29+
}
30+
31+
const files = {};
32+
1833
// Copy all component and core files to _site
19-
const files = Object.fromEntries(fs.readdirSync(path.join(repoRoot, 'elements')).map(dir => [
20-
`elements/${dir}`,
21-
`components/${dir.replace('pfe-', '')}`,
22-
]));
34+
if (hasElements) {
35+
Object.assign(files, Object.fromEntries(fs.readdirSync(path.join(repoRoot, 'elements')).map(dir => [
36+
`elements/${dir}`,
37+
`components/${dir.replace(prefix, '')}`,
38+
])));
39+
}
2340

24-
if (exists(path.join(repoRoot, 'core'))) {
41+
if (hasCore) {
2542
Object.assign(files, Object.fromEntries(fs.readdirSync(path.join(repoRoot, 'core')).map(dir => [
2643
`core/${dir}`,
27-
`core/${dir.replace('pfe-', '')}`,
44+
`core/${dir.replace(prefix, '')}`,
2845
])));
2946
}
3047

3148
return files;
3249
}
3350

3451
let didFirstBuild = false;
35-
/** Generate a single-file bundle of all pfe components and their dependencies */
36-
async function bundle() {
52+
53+
/** Generate a single-file bundle of all the repo's components and their dependencies */
54+
async function bundle(options) {
3755
if (!didFirstBuild) {
3856
const { singleFileBuild } = await import('@patternfly/pfe-tools/esbuild.js');
3957
const { pfeEnvPlugin } = await import('@patternfly/pfe-tools/esbuild-plugins/pfe-env.js');
4058

4159
await singleFileBuild({
60+
additionalPackages: options?.additionalPackages,
4261
minify: process.env.NODE_ENV === 'production' || process.env.ELEVENTY_ENV?.startsWith?.('prod'),
4362
outfile: 'docs/pfe.min.js',
4463
conditions: ['esbuild'],
4564
plugins: [
4665
pfeEnvPlugin(),
4766
]
4867
}).catch(() => void 0);
68+
4969
didFirstBuild = true;
5070
}
5171
}
@@ -71,19 +91,24 @@ function demoPaths(content) {
7191
}
7292

7393
module.exports = {
74-
configFunction(eleventyConfig) {
94+
configFunction(eleventyConfig, options) {
7595
eleventyConfig.addPassthroughCopy('docs/bundle.{js,map,ts}');
7696
eleventyConfig.addPassthroughCopy('docs/pfe.min.{map,css}');
7797
eleventyConfig.addPassthroughCopy('docs/demo.{js,map,ts}');
7898
eleventyConfig.addPassthroughCopy('docs/main.mjs');
7999
eleventyConfig.addPassthroughCopy('brand/**/*');
80-
eleventyConfig.addPassthroughCopy(getFilesToCopy());
100+
const filesToCopy = getFilesToCopy(options);
101+
if (filesToCopy) {
102+
eleventyConfig.addPassthroughCopy(filesToCopy);
103+
}
81104

82105
// The demo files are written primarily for the dev SPA (`npm start`),
83106
// so here we transform the paths found in those files to match the docs site's file structure
84107
eleventyConfig.addTransform('demo-paths', demoPaths);
85108

86109
// create /docs/pfe.min.js
87-
eleventyConfig.on('eleventy.before', bundle);
110+
eleventyConfig.on('eleventy.before', () => bundle(options));
88111
},
89112
};
113+
114+

tools/pfe-tools/demo/components.ts

Lines changed: 0 additions & 30 deletions
This file was deleted.

tools/pfe-tools/esbuild.ts

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@ import { nodeExternalsPlugin } from 'esbuild-node-externals';
1515
import { readdirSync } from 'fs';
1616
import { resolve, join, dirname } from 'path';
1717
import { fileURLToPath } from 'url';
18-
import { mkdtemp, readdir, writeFile } from 'fs/promises';
19-
20-
import os from 'os';
18+
import { mkdtemp, readdir, readFile, stat, writeFile } from 'fs/promises';
2119

2220
export interface PfeEsbuildOptions {
2321
/** Extra esbuild plugins */
@@ -39,6 +37,8 @@ export interface PfeEsbuildOptions {
3937
}
4038

4139
export interface PfeEsbuildSingleFileOptions {
40+
/** list of NPM package names to bundle alongside the repo's components */
41+
additionalPackages?: string[];
4242
outfile?: string;
4343
plugins?: Plugin[];
4444
minify?: boolean;
@@ -66,6 +66,8 @@ const ALWAYS_EXCLUDE = [
6666
'pfe-styles',
6767
];
6868

69+
const exists = (filePath: string) => stat(filePath).then(() => true, () => false);
70+
6971
/** lit-css transform plugin to process `.scss` files on-the-fly */
7072
export async function transformSass(
7173
source: string,
@@ -106,36 +108,56 @@ export function getBasePlugins({ minify }: { minify?: boolean } = {}) {
106108
}
107109

108110
/** Generate a temporary file containing namespace exports of all pfe components */
109-
export async function componentsEntryPoint(options?: { prefix: string }) {
110-
const componentDirs = await readdir(join(REPO_ROOT, 'elements'));
111+
export async function componentsEntryPoint(options?: { additionalPackages?: string[] }) {
112+
const componentDirs = await readdir(join(REPO_ROOT, 'elements')).catch(() => [] as string[]);
111113
const cacheKey = componentDirs.join('--');
114+
115+
const additionalImports =
116+
options?.additionalPackages
117+
?.reduce((acc, specifier) => `${acc}\nexport * from '${specifier}';`, '') ?? '';
118+
119+
if (!cacheKey) {
120+
return additionalImports;
121+
}
122+
112123
if (!COMPONENT_ENTRYPOINTS_CACHE.get(cacheKey)) {
113124
try {
114-
const outdir =
115-
options?.prefix ? join(REPO_ROOT, options?.prefix)
116-
: await mkdtemp(join(os.tmpdir(), 'pfe'));
117-
const tmpfile = join(outdir, 'components.ts');
118-
const imports = await Promise.all(componentDirs.reduce((acc, dir) =>
119-
`${acc}\nexport * from '@patternfly/${dir}';`, ''));
120-
await writeFile(tmpfile, imports, 'utf8');
121-
COMPONENT_ENTRYPOINTS_CACHE.set(cacheKey, tmpfile);
122-
return tmpfile;
125+
const imports = await componentDirs.reduce(async (last, dir) => {
126+
const acc = await last;
127+
const elementPath = join(REPO_ROOT, 'elements', dir);
128+
const packageJsonPath = join(elementPath, 'package.json');
129+
const isPackage = await exists(packageJsonPath);
130+
if (isPackage) {
131+
const { name } = JSON.parse(await readFile(packageJsonPath, 'utf8'));
132+
return `${acc}\nexport * from '${name}';`;
133+
} else {
134+
return `${acc}\nexport * from '${elementPath.replace(REPO_ROOT, './')}/${dir}.js';`;
135+
}
136+
}, Promise.resolve(additionalImports));
137+
138+
COMPONENT_ENTRYPOINTS_CACHE.set(cacheKey, imports);
139+
return imports;
123140
} catch (error) {
124141
console.error(error);
125142
}
126143
}
144+
127145
return COMPONENT_ENTRYPOINTS_CACHE.get(cacheKey);
128146
}
129147

130148
/** Create a single-file production bundle of all elements */
131149
export async function singleFileBuild(options?: PfeEsbuildSingleFileOptions) {
132150
try {
133-
const prefix = fileURLToPath(new URL('./demo', import.meta.url)).replace(REPO_ROOT, '');
134151
const result = await esbuild.build({
135152
absWorkingDir: REPO_ROOT,
136153
allowOverwrite: true,
137154
bundle: true,
138-
entryPoints: [await componentsEntryPoint({ prefix })],
155+
stdin: {
156+
contents: await componentsEntryPoint({ additionalPackages: options?.additionalPackages }),
157+
resolveDir: REPO_ROOT,
158+
sourcefile: 'components.ts',
159+
loader: 'ts',
160+
},
139161
format: 'esm',
140162
keepNames: true,
141163
legalComments: 'linked',

0 commit comments

Comments
 (0)