xref: /plugin/bpmnio/build/build-vendor.mjs (revision 033061be24b61e2ca1dcf4d5ade55358f0bb0818)
1*033061beSJaap de Haanimport { build } from 'esbuild';
2*033061beSJaap de Haanimport { cp, mkdir, readdir, readFile, rm, stat, writeFile } from 'node:fs/promises';
3*033061beSJaap de Haanimport path from 'node:path';
4*033061beSJaap de Haanimport { fileURLToPath } from 'node:url';
5*033061beSJaap de Haan
6*033061beSJaap de Haanconst rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
7*033061beSJaap de Haanconst nodeModulesDir = path.join(rootDir, 'node_modules');
8*033061beSJaap de Haanconst vendorDir = path.join(rootDir, 'vendor');
9*033061beSJaap de Haanconst fontDir = path.join(rootDir, 'font');
10*033061beSJaap de Haan
11*033061beSJaap de Haanconst packages = [
12*033061beSJaap de Haan  {
13*033061beSJaap de Haan    name: 'bpmn-js',
14*033061beSJaap de Haan    globalName: 'BpmnJS',
15*033061beSJaap de Haan    assetsDir: path.join('dist', 'assets'),
16*033061beSJaap de Haan    fontsDir: path.join('dist', 'assets', 'bpmn-font', 'font'),
17*033061beSJaap de Haan    fontFiles: ['bpmn.woff', 'bpmn.woff2'],
18*033061beSJaap de Haan    outputs: [
19*033061beSJaap de Haan      {
20*033061beSJaap de Haan        entryPoint: path.join(rootDir, 'build', 'vendor-entrypoints', 'bpmn-viewer.js'),
21*033061beSJaap de Haan        outFile: path.join(vendorDir, 'bpmn-js', 'dist', 'bpmn-viewer.production.min.js'),
22*033061beSJaap de Haan      },
23*033061beSJaap de Haan      {
24*033061beSJaap de Haan        entryPoint: path.join(rootDir, 'build', 'vendor-entrypoints', 'bpmn-modeler.js'),
25*033061beSJaap de Haan        outFile: path.join(vendorDir, 'bpmn-js', 'dist', 'bpmn-modeler.production.min.js'),
26*033061beSJaap de Haan      },
27*033061beSJaap de Haan    ],
28*033061beSJaap de Haan  },
29*033061beSJaap de Haan  {
30*033061beSJaap de Haan    name: 'dmn-js',
31*033061beSJaap de Haan    globalName: 'DmnJS',
32*033061beSJaap de Haan    assetsDir: path.join('dist', 'assets'),
33*033061beSJaap de Haan    fontsDir: path.join('dist', 'assets', 'dmn-font', 'font'),
34*033061beSJaap de Haan    fontFiles: ['dmn.woff', 'dmn.woff2'],
35*033061beSJaap de Haan    outputs: [
36*033061beSJaap de Haan      {
37*033061beSJaap de Haan        entryPoint: path.join(rootDir, 'build', 'vendor-entrypoints', 'dmn-viewer.js'),
38*033061beSJaap de Haan        outFile: path.join(vendorDir, 'dmn-js', 'dist', 'dmn-viewer.production.min.js'),
39*033061beSJaap de Haan      },
40*033061beSJaap de Haan      {
41*033061beSJaap de Haan        entryPoint: path.join(rootDir, 'build', 'vendor-entrypoints', 'dmn-modeler.js'),
42*033061beSJaap de Haan        outFile: path.join(vendorDir, 'dmn-js', 'dist', 'dmn-modeler.production.min.js'),
43*033061beSJaap de Haan      },
44*033061beSJaap de Haan    ],
45*033061beSJaap de Haan  },
46*033061beSJaap de Haan];
47*033061beSJaap de Haan
48*033061beSJaap de Haanasync function pathExists(targetPath) {
49*033061beSJaap de Haan  try {
50*033061beSJaap de Haan    await stat(targetPath);
51*033061beSJaap de Haan    return true;
52*033061beSJaap de Haan  } catch {
53*033061beSJaap de Haan    return false;
54*033061beSJaap de Haan  }
55*033061beSJaap de Haan}
56*033061beSJaap de Haan
57*033061beSJaap de Haanasync function ensureInstalled(packageName) {
58*033061beSJaap de Haan  const packagePath = path.join(nodeModulesDir, packageName, 'package.json');
59*033061beSJaap de Haan
60*033061beSJaap de Haan  if (!(await pathExists(packagePath))) {
61*033061beSJaap de Haan    throw new Error(
62*033061beSJaap de Haan      `Missing npm dependency ${packageName}. Run npm install before building vendor bundles.`
63*033061beSJaap de Haan    );
64*033061beSJaap de Haan  }
65*033061beSJaap de Haan
66*033061beSJaap de Haan  return packagePath;
67*033061beSJaap de Haan}
68*033061beSJaap de Haan
69*033061beSJaap de Haanasync function readPackageMetadata(packageName) {
70*033061beSJaap de Haan  const packagePath = await ensureInstalled(packageName);
71*033061beSJaap de Haan  return JSON.parse(await readFile(packagePath, 'utf8'));
72*033061beSJaap de Haan}
73*033061beSJaap de Haan
74*033061beSJaap de Haanfunction createBanner(metadata) {
75*033061beSJaap de Haan  return `/*! ${metadata.name} - ${metadata.version} | generated for dokuwiki-plugin-bpmnio | ${metadata.license} */`;
76*033061beSJaap de Haan}
77*033061beSJaap de Haan
78*033061beSJaap de Haanasync function copyFileEnsuringDir(sourcePath, targetPath) {
79*033061beSJaap de Haan  await mkdir(path.dirname(targetPath), { recursive: true });
80*033061beSJaap de Haan  await cp(sourcePath, targetPath, { force: true });
81*033061beSJaap de Haan}
82*033061beSJaap de Haan
83*033061beSJaap de Haanasync function copyAssets(sourceDir, targetDir) {
84*033061beSJaap de Haan  await mkdir(targetDir, { recursive: true });
85*033061beSJaap de Haan
86*033061beSJaap de Haan  for (const entry of await readdir(sourceDir, { withFileTypes: true })) {
87*033061beSJaap de Haan    const sourcePath = path.join(sourceDir, entry.name);
88*033061beSJaap de Haan    const normalizedPath = sourcePath.split(path.sep).join('/');
89*033061beSJaap de Haan
90*033061beSJaap de Haan    if (normalizedPath.includes('/font/')) {
91*033061beSJaap de Haan      continue;
92*033061beSJaap de Haan    }
93*033061beSJaap de Haan
94*033061beSJaap de Haan    if (entry.isDirectory()) {
95*033061beSJaap de Haan      await copyAssets(sourcePath, path.join(targetDir, entry.name));
96*033061beSJaap de Haan      continue;
97*033061beSJaap de Haan    }
98*033061beSJaap de Haan
99*033061beSJaap de Haan    const targetName = entry.name.endsWith('.css')
100*033061beSJaap de Haan      ? entry.name.replace(/\.css$/u, '.less')
101*033061beSJaap de Haan      : entry.name;
102*033061beSJaap de Haan
103*033061beSJaap de Haan    await copyFileEnsuringDir(sourcePath, path.join(targetDir, targetName));
104*033061beSJaap de Haan  }
105*033061beSJaap de Haan}
106*033061beSJaap de Haan
107*033061beSJaap de Haanasync function copyFonts(sourceDir, files) {
108*033061beSJaap de Haan  await mkdir(fontDir, { recursive: true });
109*033061beSJaap de Haan
110*033061beSJaap de Haan  for (const file of files) {
111*033061beSJaap de Haan    await copyFileEnsuringDir(path.join(sourceDir, file), path.join(fontDir, file));
112*033061beSJaap de Haan  }
113*033061beSJaap de Haan}
114*033061beSJaap de Haan
115*033061beSJaap de Haanasync function cleanPackageOutput(packageName) {
116*033061beSJaap de Haan  await rm(path.join(vendorDir, packageName), { recursive: true, force: true });
117*033061beSJaap de Haan}
118*033061beSJaap de Haan
119*033061beSJaap de Haanasync function copyMetadata(packageName) {
120*033061beSJaap de Haan  const sourceDir = path.join(nodeModulesDir, packageName);
121*033061beSJaap de Haan  const targetDir = path.join(vendorDir, packageName);
122*033061beSJaap de Haan
123*033061beSJaap de Haan  for (const file of ['LICENSE', 'README.md', 'package.json']) {
124*033061beSJaap de Haan    const sourcePath = path.join(sourceDir, file);
125*033061beSJaap de Haan    if (await pathExists(sourcePath)) {
126*033061beSJaap de Haan      await copyFileEnsuringDir(sourcePath, path.join(targetDir, file));
127*033061beSJaap de Haan    }
128*033061beSJaap de Haan  }
129*033061beSJaap de Haan}
130*033061beSJaap de Haan
131*033061beSJaap de Haanasync function buildBundle({ entryPoint, outFile, metadata, packageName }) {
132*033061beSJaap de Haan  await mkdir(path.dirname(outFile), { recursive: true });
133*033061beSJaap de Haan
134*033061beSJaap de Haan  await build({
135*033061beSJaap de Haan    entryPoints: [entryPoint],
136*033061beSJaap de Haan    outfile: outFile,
137*033061beSJaap de Haan    bundle: true,
138*033061beSJaap de Haan    minify: true,
139*033061beSJaap de Haan    platform: 'browser',
140*033061beSJaap de Haan    format: 'iife',
141*033061beSJaap de Haan    target: ['es2019'],
142*033061beSJaap de Haan    legalComments: 'inline',
143*033061beSJaap de Haan    banner: {
144*033061beSJaap de Haan      js: createBanner(metadata),
145*033061beSJaap de Haan    },
146*033061beSJaap de Haan    define: {
147*033061beSJaap de Haan      'process.env.NODE_ENV': '"production"',
148*033061beSJaap de Haan      global: 'window',
149*033061beSJaap de Haan    },
150*033061beSJaap de Haan    logLevel: 'info',
151*033061beSJaap de Haan  });
152*033061beSJaap de Haan
153*033061beSJaap de Haan  console.log(`Built ${packageName} bundle: ${path.relative(rootDir, outFile)}`);
154*033061beSJaap de Haan}
155*033061beSJaap de Haan
156*033061beSJaap de Haanasync function main() {
157*033061beSJaap de Haan  for (const pkg of packages) {
158*033061beSJaap de Haan    const metadata = await readPackageMetadata(pkg.name);
159*033061beSJaap de Haan    const sourceDir = path.join(nodeModulesDir, pkg.name);
160*033061beSJaap de Haan    const sourceAssetsDir = path.join(sourceDir, pkg.assetsDir);
161*033061beSJaap de Haan    const sourceFontsDir = path.join(sourceDir, pkg.fontsDir);
162*033061beSJaap de Haan    const targetAssetsDir = path.join(vendorDir, pkg.name, pkg.assetsDir);
163*033061beSJaap de Haan
164*033061beSJaap de Haan    await cleanPackageOutput(pkg.name);
165*033061beSJaap de Haan    await copyMetadata(pkg.name);
166*033061beSJaap de Haan    await copyAssets(sourceAssetsDir, targetAssetsDir);
167*033061beSJaap de Haan    await copyFonts(sourceFontsDir, pkg.fontFiles);
168*033061beSJaap de Haan
169*033061beSJaap de Haan    for (const output of pkg.outputs) {
170*033061beSJaap de Haan      await buildBundle({
171*033061beSJaap de Haan        entryPoint: output.entryPoint,
172*033061beSJaap de Haan        outFile: output.outFile,
173*033061beSJaap de Haan        metadata,
174*033061beSJaap de Haan        packageName: pkg.name,
175*033061beSJaap de Haan      });
176*033061beSJaap de Haan    }
177*033061beSJaap de Haan  }
178*033061beSJaap de Haan
179*033061beSJaap de Haan  const generatedMetadata = {
180*033061beSJaap de Haan    generatedAt: new Date().toISOString(),
181*033061beSJaap de Haan    packages: Object.fromEntries(
182*033061beSJaap de Haan      await Promise.all(
183*033061beSJaap de Haan        packages.map(async (pkg) => [pkg.name, (await readPackageMetadata(pkg.name)).version])
184*033061beSJaap de Haan      )
185*033061beSJaap de Haan    ),
186*033061beSJaap de Haan  };
187*033061beSJaap de Haan
188*033061beSJaap de Haan  await writeFile(
189*033061beSJaap de Haan    path.join(vendorDir, 'build-manifest.json'),
190*033061beSJaap de Haan    `${JSON.stringify(generatedMetadata, null, 2)}\n`
191*033061beSJaap de Haan  );
192*033061beSJaap de Haan}
193*033061beSJaap de Haan
194*033061beSJaap de Haanmain().catch((error) => {
195*033061beSJaap de Haan  console.error(error.message);
196*033061beSJaap de Haan  process.exitCode = 1;
197*033061beSJaap de Haan});
198