From 57b13d848a7ec5a7cbfaf8460934b29d6339a6c3 Mon Sep 17 00:00:00 2001 From: Cory LaViska Date: Wed, 7 Jun 2023 13:28:22 -0400 Subject: [PATCH] nicer build (abort still erroring) --- docs/eleventy.config.cjs | 3 +- package-lock.json | 141 ++++++--------- package.json | 3 +- scripts/build.js | 358 ++++++++++++++++++++++---------------- scripts/make-icons.js | 99 +++++------ scripts/make-metadata.js | 5 +- scripts/make-react.js | 6 - scripts/make-themes.js | 2 - scripts/make-web-types.js | 2 - 9 files changed, 316 insertions(+), 303 deletions(-) diff --git a/docs/eleventy.config.cjs b/docs/eleventy.config.cjs index 1244cb495..a6eb32661 100644 --- a/docs/eleventy.config.cjs +++ b/docs/eleventy.config.cjs @@ -150,7 +150,7 @@ module.exports = function (eleventyConfig) { const map = {}; const searchIndexFilename = path.join(eleventyConfig.dir.output, assetsDir, 'search.json'); - const lunrInput = '../node_modules/lunr/lunr.min.js'; + const lunrInput = path.resolve('../node_modules/lunr/lunr.min.js'); const lunrOutput = path.join(eleventyConfig.dir.output, assetsDir, 'scripts/lunr.js'); const searchIndex = lunr(function () { // The search index uses these field names extensively, so shortening them can save some serious bytes. The @@ -188,6 +188,7 @@ module.exports = function (eleventyConfig) { }); // Copy the Lunr search client and write the index + fs.mkdirSync(path.dirname(lunrOutput), { recursive: true }); fs.copyFileSync(lunrInput, lunrOutput); fs.writeFileSync(searchIndexFilename, JSON.stringify({ searchIndex, map }), 'utf-8'); diff --git a/package-lock.json b/package-lock.json index 5d7a98eec..b30e6af12 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ }, "devDependencies": { "@11ty/eleventy": "^2.0.1", - "@custom-elements-manifest/analyzer": "^0.6.8", + "@custom-elements-manifest/analyzer": "^0.8.3", "@open-wc/testing": "^3.1.7", "@types/mocha": "^10.0.1", "@types/react": "^18.0.26", @@ -65,6 +65,7 @@ "markdown-it-mark": "^3.0.1", "markdown-it-replace-it": "^1.0.0", "npm-check-updates": "^16.6.2", + "ora": "^6.3.1", "pascal-case": "^3.1.2", "plop": "^3.1.1", "prettier": "^2.8.8", @@ -830,9 +831,9 @@ } }, "node_modules/@custom-elements-manifest/analyzer": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/@custom-elements-manifest/analyzer/-/analyzer-0.6.8.tgz", - "integrity": "sha512-Wy5CiMUq9njT6vbeEPi7Zjm7OcVmL+5zyJXUam5TZe13QVnsn/m3VaJx8aqMecTp1m/pFNCL4QGJpYV/oZYIkg==", + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@custom-elements-manifest/analyzer/-/analyzer-0.8.3.tgz", + "integrity": "sha512-wT+t4JNVMj56bSCNxxjkDSKx2KJ6bzXQMjinXJe/2+9J7Abaw/9a4+EWYLWQ//KBXRvyNhWrkolFL6ICc/VATg==", "dev": true, "dependencies": { "@custom-elements-manifest/find-dependencies": "^0.0.5", @@ -3914,9 +3915,9 @@ } }, "node_modules/bl": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-5.0.0.tgz", - "integrity": "sha512-8vxFNZ0pflFfi0WXA3WQXlj6CaMEwsmh63I1CNp0q+wWv8sD0ARx1KovSQd0l2GkwrMIOyedq0EF1FxI+RCZLQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", + "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", "dev": true, "dependencies": { "buffer": "^6.0.3", @@ -3925,9 +3926,9 @@ } }, "node_modules/bl/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, "dependencies": { "inherits": "^2.0.3", @@ -12830,18 +12831,18 @@ } }, "node_modules/ora": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-6.0.1.tgz", - "integrity": "sha512-TDdKkKHdWE6jo/6pIa5U5AWcSVfpNRFJ8sdRJpioGNVPLAzZzHs/N+QhUfF7ZbyoC+rnDuNTKzeDJUbAza9g4g==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-6.3.1.tgz", + "integrity": "sha512-ERAyNnZOfqM+Ao3RAvIXkYh5joP220yf59gVe2X/cI6SiCxIdi4c9HZKZD8R6q/RDXEje1THBju6iExiSsgJaQ==", "dev": true, "dependencies": { - "bl": "^5.0.0", - "chalk": "^4.1.2", + "chalk": "^5.0.0", "cli-cursor": "^4.0.0", - "cli-spinners": "^2.6.0", + "cli-spinners": "^2.6.1", "is-interactive": "^2.0.0", "is-unicode-supported": "^1.1.0", - "log-symbols": "^5.0.0", + "log-symbols": "^5.1.0", + "stdin-discarder": "^0.1.0", "strip-ansi": "^7.0.1", "wcwidth": "^1.0.1" }, @@ -12864,37 +12865,6 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/ora/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/ora/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/ora/node_modules/cli-cursor": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", @@ -15557,6 +15527,21 @@ "node": ">= 0.6" } }, + "node_modules/stdin-discarder": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz", + "integrity": "sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==", + "dev": true, + "dependencies": { + "bl": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/stop-iteration-iterator": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", @@ -17862,9 +17847,9 @@ "integrity": "sha512-tlJpwF40DEQcfR/QF+wNMVyGMaO9FQp6Z1Wahj4Gk3CJQYHwA2xVG7iKDFdW6zuxZY9XWOpGcfNCTsX4McOsOg==" }, "@custom-elements-manifest/analyzer": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/@custom-elements-manifest/analyzer/-/analyzer-0.6.8.tgz", - "integrity": "sha512-Wy5CiMUq9njT6vbeEPi7Zjm7OcVmL+5zyJXUam5TZe13QVnsn/m3VaJx8aqMecTp1m/pFNCL4QGJpYV/oZYIkg==", + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@custom-elements-manifest/analyzer/-/analyzer-0.8.3.tgz", + "integrity": "sha512-wT+t4JNVMj56bSCNxxjkDSKx2KJ6bzXQMjinXJe/2+9J7Abaw/9a4+EWYLWQ//KBXRvyNhWrkolFL6ICc/VATg==", "dev": true, "requires": { "@custom-elements-manifest/find-dependencies": "^0.0.5", @@ -20109,9 +20094,9 @@ "dev": true }, "bl": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-5.0.0.tgz", - "integrity": "sha512-8vxFNZ0pflFfi0WXA3WQXlj6CaMEwsmh63I1CNp0q+wWv8sD0ARx1KovSQd0l2GkwrMIOyedq0EF1FxI+RCZLQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", + "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", "dev": true, "requires": { "buffer": "^6.0.3", @@ -20120,9 +20105,9 @@ }, "dependencies": { "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, "requires": { "inherits": "^2.0.3", @@ -26908,18 +26893,18 @@ } }, "ora": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-6.0.1.tgz", - "integrity": "sha512-TDdKkKHdWE6jo/6pIa5U5AWcSVfpNRFJ8sdRJpioGNVPLAzZzHs/N+QhUfF7ZbyoC+rnDuNTKzeDJUbAza9g4g==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-6.3.1.tgz", + "integrity": "sha512-ERAyNnZOfqM+Ao3RAvIXkYh5joP220yf59gVe2X/cI6SiCxIdi4c9HZKZD8R6q/RDXEje1THBju6iExiSsgJaQ==", "dev": true, "requires": { - "bl": "^5.0.0", - "chalk": "^4.1.2", + "chalk": "^5.0.0", "cli-cursor": "^4.0.0", - "cli-spinners": "^2.6.0", + "cli-spinners": "^2.6.1", "is-interactive": "^2.0.0", "is-unicode-supported": "^1.1.0", - "log-symbols": "^5.0.0", + "log-symbols": "^5.1.0", + "stdin-discarder": "^0.1.0", "strip-ansi": "^7.0.1", "wcwidth": "^1.0.1" }, @@ -26930,25 +26915,6 @@ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, "cli-cursor": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", @@ -29015,6 +28981,15 @@ "integrity": "sha512-wuTCPGlJONk/a1kqZ4fQM2+908lC7fa7nPYpTC1EhnvqLX/IICbeP1OZGDtA374trpSq68YubKUMo8oRhN46yg==", "dev": true }, + "stdin-discarder": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz", + "integrity": "sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==", + "dev": true, + "requires": { + "bl": "^5.0.0" + } + }, "stop-iteration-iterator": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", diff --git a/package.json b/package.json index 9091fba41..7a7814ca0 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ }, "devDependencies": { "@11ty/eleventy": "^2.0.1", - "@custom-elements-manifest/analyzer": "^0.6.8", + "@custom-elements-manifest/analyzer": "^0.8.3", "@open-wc/testing": "^3.1.7", "@types/mocha": "^10.0.1", "@types/react": "^18.0.26", @@ -119,6 +119,7 @@ "markdown-it-mark": "^3.0.1", "markdown-it-replace-it": "^1.0.0", "npm-check-updates": "^16.6.2", + "ora": "^6.3.1", "pascal-case": "^3.1.2", "plop": "^3.1.1", "prettier": "^2.8.8", diff --git a/scripts/build.js b/scripts/build.js index 122e90789..17cf337db 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -1,194 +1,248 @@ -import { deleteSync } from 'del'; +import { deleteAsync } from 'del'; +import { exec as execCallback, spawn as spawnCallback } from 'child_process'; import { globby } from 'globby'; -import { execSync, spawn } from 'child_process'; import browserSync from 'browser-sync'; import chalk from 'chalk'; import chokidar from 'chokidar'; import commandLineArgs from 'command-line-args'; import copy from 'recursive-copy'; import esbuild from 'esbuild'; -import fs from 'fs'; +import fs from 'fs/promises'; import getPort, { portNumbers } from 'get-port'; +import ora from 'ora'; +import util from 'util'; +const exec = util.promisify(execCallback); +const spawn = util.promisify(spawnCallback); + +const outdir = 'dist'; const abortController = new AbortController(); const abortSignal = abortController.signal; +const spinner = ora().start(); +let buildResult; -function buildTheDocs(watch = false) { - deleteSync('./_site'); +const { bundle, copydir, dir, serve, types } = commandLineArgs([ + { name: 'bundle', type: Boolean }, + { name: 'copydir', type: String }, + { name: 'serve', type: Boolean }, + { name: 'types', type: Boolean } +]); + +async function buildTheDocs(watch = false) { + await deleteAsync('_site'); if (!watch) { - return execSync('npx @11ty/eleventy --quiet', { stdio: 'inherit', cwd: 'docs' }); + return execCallback('npx @11ty/eleventy --quiet', { stdio: 'inherit', cwd: 'docs', signal: abortSignal }); } - return spawn('npx', ['@11ty/eleventy', '--watch', '--incremental', '--quiet'], { - stdio: 'inherit', + return spawnCallback('npx', ['@11ty/eleventy', '--watch', '--incremental', '--quiet'], { + stdio: 'pipe', cwd: 'docs', signal: abortSignal }); } -const { bundle, copydir, dir, serve, types } = commandLineArgs([ - { name: 'bundle', type: Boolean }, - { name: 'copydir', type: String }, - { name: 'dir', type: String, defaultValue: 'dist' }, - { name: 'serve', type: Boolean }, - { name: 'types', type: Boolean } -]); +async function buildTheSource() { + const alwaysExternal = ['@lit-labs/react', 'react']; + return await esbuild.build({ + format: 'esm', + target: 'es2017', + entryPoints: [ + // + // NOTE: Entry points must be mapped in package.json > exports, otherwise users won't be able to import them! + // + // The whole shebang + './src/shoelace.ts', + // The auto-loader + './src/shoelace-autoloader.ts', + // Components + ...(await globby('./src/components/**/!(*.(style|test)).ts')), + // Translations + ...(await globby('./src/translations/**/*.ts')), + // Public utilities + ...(await globby('./src/utilities/**/!(*.(style|test)).ts')), + // Theme stylesheets + ...(await globby('./src/themes/**/!(*.test).ts')), + // React wrappers + ...(await globby('./src/react/**/*.ts')) + ], + outdir, + chunkNames: 'chunks/[name].[hash]', + incremental: serve, + define: { + // Floating UI requires this to be set + 'process.env.NODE_ENV': '"production"' + }, + bundle: true, + // + // We don't bundle certain dependencies in the unbundled build. This ensures we ship bare module specifiers, + // allowing end users to better optimize when using a bundler. (Only packages that ship ESM can be external.) + // + // We never bundle React or @lit-labs/react though! + // + external: bundle + ? alwaysExternal + : [...alwaysExternal, '@floating-ui/dom', '@shoelace-style/animations', 'lit', 'qr-creator'], + splitting: true, + plugins: [] + }); +} -const outdir = dir; +function handleCleanup() { + buildResult.rebuild.dispose(); + abortController.abort(); // Stops the child process +} -deleteSync(outdir); -fs.mkdirSync(outdir, { recursive: true }); +async function nextTask(label, action) { + spinner.text = label; + spinner.start(); -(async () => { try { - execSync(`node scripts/make-metadata.js --outdir "${outdir}"`, { stdio: 'inherit' }); - execSync(`node scripts/make-react.js --outdir "${outdir}"`, { stdio: 'inherit' }); - execSync(`node scripts/make-web-types.js --outdir "${outdir}"`, { stdio: 'inherit' }); - execSync(`node scripts/make-themes.js --outdir "${outdir}"`, { stdio: 'inherit' }); - execSync(`node scripts/make-icons.js --outdir "${outdir}"`, { stdio: 'inherit' }); - if (types) { - console.log('Running the TypeScript compiler...'); - execSync(`tsc --project ./tsconfig.prod.json --outdir "${outdir}"`, { stdio: 'inherit' }); - } + await action(); + spinner.stop(); + console.log(`${chalk.green('āœ”')} ${label}`); } catch (err) { - console.error(chalk.red(err)); + spinner.stop(); + console.error(`${chalk.red('✘')} ${err}`); process.exit(1); } +} - const alwaysExternal = ['@lit-labs/react', 'react']; - const buildResult = await esbuild - .build({ - format: 'esm', - target: 'es2017', - entryPoints: [ - // - // NOTE: Entry points must be mapped in package.json > exports, otherwise users won't be able to import them! - // - // The whole shebang - './src/shoelace.ts', - // The auto-loader - './src/shoelace-autoloader.ts', - // Components - ...(await globby('./src/components/**/!(*.(style|test)).ts')), - // Translations - ...(await globby('./src/translations/**/*.ts')), - // Public utilities - ...(await globby('./src/utilities/**/!(*.(style|test)).ts')), - // Theme stylesheets - ...(await globby('./src/themes/**/!(*.test).ts')), - // React wrappers - ...(await globby('./src/react/**/*.ts')) - ], - outdir, - chunkNames: 'chunks/[name].[hash]', - incremental: serve, - define: { - // Floating UI requires this to be set - 'process.env.NODE_ENV': '"production"' - }, - bundle: true, - // - // We don't bundle certain dependencies in the unbundled build. This ensures we ship bare module specifiers, - // allowing end users to better optimize when using a bundler. (Only packages that ship ESM can be external.) - // - // We never bundle React or @lit-labs/react though! - // - external: bundle - ? alwaysExternal - : [...alwaysExternal, '@floating-ui/dom', '@shoelace-style/animations', 'lit', 'qr-creator'], - splitting: true, - plugins: [] - }) - .catch(err => { - console.error(chalk.red(err)); - process.exit(1); - }); +await nextTask('Cleaning up the previous build', async () => { + await deleteAsync(outdir); + await fs.mkdir(outdir, { recursive: true }); +}); - // Copy the build output to an additional directory - if (copydir) { - deleteSync(copydir); - copy(outdir, copydir); - } +await nextTask('Generating component metadata', () => { + return exec(`node scripts/make-metadata.js --outdir "${outdir}"`, { stdio: 'inherit' }); +}); - if (serve) { - // Build it with --watch and --incremental - buildTheDocs(true); +await nextTask('Wrapping components for React', () => { + return exec(`node scripts/make-react.js --outdir "${outdir}"`, { stdio: 'inherit' }); +}); - // Wait for the search index to appear before launching the browser. This file is generated during eleventy.after, - // so it's usually the last one to appear. - const watcher = chokidar.watch('./_site', { persistent: true }); - watcher.on('add', async filename => { - if (filename.endsWith('search.json')) { - watcher.close(); +await nextTask('Generating Web Types', () => { + return exec(`node scripts/make-web-types.js --outdir "${outdir}"`, { stdio: 'inherit' }); +}); - const bs = browserSync.create(); - const port = await getPort({ - port: portNumbers(4000, 4999) - }); +await nextTask('Generating themes', () => { + return exec(`node scripts/make-themes.js --outdir "${outdir}"`, { stdio: 'inherit' }); +}); - const browserSyncConfig = { - startPath: '/', - port, - logLevel: 'silent', - logPrefix: '[shoelace]', - logFileChanges: true, - notify: false, - single: false, - ghostMode: false, - server: { - baseDir: '_site', - routes: { - '/dist': './dist' - } - } - }; +await nextTask('Packaging up icons', () => { + return exec(`node scripts/make-icons.js --outdir "${outdir}"`, { stdio: 'inherit' }); +}); - // Launch browser sync - bs.init(browserSyncConfig, () => { - const url = `http://localhost:${port}`; - console.log(chalk.cyan(`Launched the Shoelace dev server at ${url} 🄾\n`)); - }); +await nextTask('Running the TypeScript compiler', () => { + return exec(`tsc --project ./tsconfig.prod.json --outdir "${outdir}"`, { stdio: 'inherit' }); +}); - // Rebuild and reload when source files change - bs.watch(['src/**/!(*.test).*']).on('change', async filename => { - buildResult - // Rebuild and reload - .rebuild() - .then(() => { - // Rebuild stylesheets when a theme file changes - if (/^src\/themes/.test(filename)) { - execSync(`node scripts/make-themes.js --outdir "${outdir}"`, { stdio: 'inherit' }); - } - }) - .then(() => { - // Skip metadata when styles are changed - if (/(\.css|\.styles\.ts)$/.test(filename)) { - return; - } +await nextTask('Building source files', async () => { + return (buildResult = await buildTheSource()); +}); - execSync(`node scripts/make-metadata.js --outdir "${outdir}"`, { stdio: 'inherit' }); - }) - .then(() => bs.reload()) - .catch(err => console.error(chalk.red(err))); - }); +// Copy the build output to an additional directory +if (copydir) { + await nextTask(`Copying the build to "${copydir}"`, async () => { + await deleteAsync(copydir); + await copy(outdir, copydir); + }); +} - // Reload without rebuilding when the docs change - bs.watch(['_site/**/*.*']).on('change', () => { - bs.reload(); - }); +if (serve) { + const deferredOutput = []; + let hasBuilt = false; + + // Spin up Eleventy and Wait for the search index to appear before proceeding. The search index is generated during + // eleventy.after, so it appears after the docs are fully published. This is kinda hacky, but here we are. + // Kick off the Eleventy dev server with --watch and --incremental + await nextTask('Building docs', async () => { + const child = await buildTheDocs(true); + + // Store Eleventy's output for later + child.stdout.on('data', data => { + if (hasBuilt) { + console.log(data.toString()); + } else { + deferredOutput.push(data.toString()); } }); - } - // Prod build - if (!serve) { - buildTheDocs(); - } - - // Cleanup on exit - process.on('SIGTERM', () => { - buildResult.rebuild.dispose(); - abortController.abort(); // Stops the child process + return new Promise(resolve => { + const watcher = chokidar.watch('_site', { persistent: true }); + watcher.on('add', async filename => { + if (filename.endsWith('search.json')) { + await watcher.close(); + resolve(); + } + }); + }); }); -})(); + + const bs = browserSync.create(); + const port = await getPort({ port: portNumbers(4000, 4999) }); + const browserSyncConfig = { + startPath: '/', + port, + logLevel: 'silent', + logPrefix: '[shoelace]', + logFileChanges: true, + notify: false, + single: false, + ghostMode: false, + server: { + baseDir: '_site', + routes: { + '/dist': './dist' + } + } + }; + + // Launch browser sync + bs.init(browserSyncConfig, () => { + const url = `http://localhost:${port}`; + console.log(chalk.cyan(`\n🄾 The dev server is available at ${url}\n`)); + console.log(deferredOutput.join('\n')); + hasBuilt = true; + }); + + // Rebuild and reload when source files change + bs.watch(['src/**/!(*.test).*']).on('change', async filename => { + try { + // Rebuild and reload + await buildResult.rebuild(); + + // Rebuild stylesheets when a theme file changes + if (/^src\/themes/.test(filename)) { + await exec(`node scripts/make-themes.js --outdir "${outdir}"`, { stdio: 'inherit' }); + } + + // Skip metadata when styles are changed + if (/(\.css|\.styles\.ts)$/.test(filename)) { + return; + } + + await exec(`node scripts/make-metadata.js --outdir "${outdir}"`, { stdio: 'inherit' }); + + bs.reload(); + } catch (err) { + console.error(chalk.red(err)); + } + }); + + // Reload without rebuilding when the docs change + bs.watch(['_site/**/*.*']).on('change', filename => { + bs.reload(); + }); +} + +// Prod build +if (!serve) { + await nextTask('Building the docs', () => { + return buildTheDocs(); + }); +} + +// Cleanup on exit +process.on('SIGINT', handleCleanup); +process.on('SIGTERM', handleCleanup); diff --git a/scripts/make-icons.js b/scripts/make-icons.js index e5b4fe6eb..aabfd8566 100644 --- a/scripts/make-icons.js +++ b/scripts/make-icons.js @@ -7,65 +7,58 @@ import copy from 'recursive-copy'; import { deleteAsync } from 'del'; import download from 'download'; import fm from 'front-matter'; -import { readFileSync, mkdirSync } from 'fs'; -import { stat, readFile, writeFile } from 'fs/promises'; +import fs from 'fs/promises'; import { globby } from 'globby'; import path from 'path'; const { outdir } = commandLineArgs({ name: 'outdir', type: String }); const iconDir = path.join(outdir, '/assets/icons'); -const iconPackageData = JSON.parse(readFileSync('./node_modules/bootstrap-icons/package.json', 'utf8')); -let numIcons = 0; +const iconPackageData = JSON.parse(await fs.readFile('./node_modules/bootstrap-icons/package.json', 'utf8')); + +try { + const version = iconPackageData.version; + const srcPath = `./.cache/icons/icons-${version}`; + const url = `https://github.com/twbs/icons/archive/v${version}.zip`; -(async () => { try { - const version = iconPackageData.version; - const srcPath = `./.cache/icons/icons-${version}`; - const url = `https://github.com/twbs/icons/archive/v${version}.zip`; - - try { - await stat(`${srcPath}/LICENSE.md`); - console.log('Generating icons from cache'); - } catch { - // Download the source from GitHub (since not everything is published to NPM) - console.log(`Downloading and extracting Bootstrap Icons ${version} šŸ“¦`); - await download(url, './.cache/icons', { extract: true }); - } - - // Copy icons - console.log(`Copying icons and license`); - await deleteAsync([iconDir]); - mkdirSync(iconDir, { recursive: true }); - await Promise.all([ - copy(`${srcPath}/icons`, iconDir), - copy(`${srcPath}/LICENSE.md`, path.join(iconDir, 'LICENSE.md')), - copy(`${srcPath}/bootstrap-icons.svg`, './docs/assets/images/sprite.svg', { overwrite: true }) - ]); - - // Generate metadata - console.log(`Generating icon metadata`); - const files = await globby(`${srcPath}/docs/content/icons/**/*.md`); - - const metadata = await Promise.all( - files.map(async file => { - const name = path.basename(file, path.extname(file)); - const data = fm(await readFile(file, 'utf8')).attributes; - numIcons++; - - return { - name, - title: data.title, - categories: data.categories, - tags: data.tags - }; - }) - ); - - await writeFile(path.join(iconDir, 'icons.json'), JSON.stringify(metadata, null, 2), 'utf8'); - - console.log(chalk.cyan(`Successfully processed ${numIcons} icons ✨\n`)); - } catch (err) { - console.error(err); + await fs.stat(`${srcPath}/LICENSE.md`); + } catch { + // Download the source from GitHub (since not everything is published to NPM) + console.log(`Downloading and extracting Bootstrap Icons ${version}...`); + await download(url, './.cache/icons', { extract: true }); } -})(); + + // Copy icons + console.log(`Copying icons and license...`); + await deleteAsync([iconDir]); + await fs.mkdir(iconDir, { recursive: true }); + await Promise.all([ + copy(`${srcPath}/icons`, iconDir), + copy(`${srcPath}/LICENSE.md`, path.join(iconDir, 'LICENSE.md')), + copy(`${srcPath}/bootstrap-icons.svg`, './docs/assets/images/sprite.svg', { overwrite: true }) + ]); + + // Generate metadata + const files = await globby(`${srcPath}/docs/content/icons/**/*.md`); + + console.log(`Generating metadata for ${files.length} icons...`); + + const metadata = await Promise.all( + files.map(async file => { + const name = path.basename(file, path.extname(file)); + const data = fm(await fs.readFile(file, 'utf8')).attributes; + + return { + name, + title: data.title, + categories: data.categories, + tags: data.tags + }; + }) + ); + + await fs.writeFile(path.join(iconDir, 'icons.json'), JSON.stringify(metadata, null, 2), 'utf8'); +} catch (err) { + console.error(err); +} diff --git a/scripts/make-metadata.js b/scripts/make-metadata.js index e784695a7..546ddf414 100644 --- a/scripts/make-metadata.js +++ b/scripts/make-metadata.js @@ -1,11 +1,10 @@ // // This script runs the Custom Elements Manifest analyzer to generate custom-elements.json // + import { execSync } from 'child_process'; import commandLineArgs from 'command-line-args'; const { outdir } = commandLineArgs({ name: 'outdir', type: String }); -// Run the analyzer -console.log('Generating component metadata'); -execSync(`cem analyze --litelement --outdir "${outdir}"`, { stdio: 'inherit' }); +await execSync(`cem analyze --litelement --outdir "${outdir}"`, { stdio: 'inherit' }); diff --git a/scripts/make-react.js b/scripts/make-react.js index efcf5339c..fff87269d 100644 --- a/scripts/make-react.js +++ b/scripts/make-react.js @@ -17,10 +17,6 @@ fs.mkdirSync(reactDir, { recursive: true }); // Fetch component metadata const metadata = JSON.parse(fs.readFileSync(path.join(outdir, 'custom-elements.json'), 'utf8')); - -// Wrap components -console.log('Wrapping components for React...'); - const components = getAllComponents(metadata); const index = []; @@ -60,5 +56,3 @@ components.map(component => { // Generate the index file fs.writeFileSync(path.join(reactDir, 'index.ts'), index.join('\n'), 'utf8'); - -console.log(chalk.cyan(`\nComponents have been wrapped for React! šŸ“¦\n`)); diff --git a/scripts/make-themes.js b/scripts/make-themes.js index 9d266967c..a8077c42b 100644 --- a/scripts/make-themes.js +++ b/scripts/make-themes.js @@ -16,8 +16,6 @@ const filesToEmbed = globbySync('./src/themes/**/_*.css'); const themesDir = path.join(outdir, 'themes'); const embeds = {}; -console.log('Generating stylesheets'); - mkdirSync(themesDir, { recursive: true }); try { diff --git a/scripts/make-web-types.js b/scripts/make-web-types.js index 4e0c6c742..156ff2ba2 100644 --- a/scripts/make-web-types.js +++ b/scripts/make-web-types.js @@ -62,9 +62,7 @@ const jsonataExprString = `{ } }`; -// Run the conversion const expression = jsonata(jsonataExprString); const result = await expression.evaluate(metadata); -console.log('Generating web types'); fs.writeFileSync(path.join(outdir, 'web-types.json'), JSON.stringify(result, null, 2), 'utf8');