diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md index 576b17dc8..8f1b6ac9c 100644 --- a/docs/getting-started/installation.md +++ b/docs/getting-started/installation.md @@ -8,7 +8,7 @@ The easiest way to install Shoelace is with the CDN. Just add the following tags ```html - + ``` ### Dark Theme @@ -17,7 +17,7 @@ If you prefer to use the dark theme instead, use this. Note the `sl-theme-dark` ```html - + ``` ### Light & Dark Theme @@ -29,7 +29,7 @@ If you want to load the light or dark theme based on the user's `prefers-color-s - + ``` Now you can [start using Shoelace!](/getting-started/usage) diff --git a/docs/getting-started/overview.md b/docs/getting-started/overview.md index da3a9666d..465e7082d 100644 --- a/docs/getting-started/overview.md +++ b/docs/getting-started/overview.md @@ -32,7 +32,7 @@ Add the following code to your page. ```html - + ``` Now you have access to all of Shoelace's components! Try adding a button: diff --git a/docs/resources/changelog.md b/docs/resources/changelog.md index 0c92aa693..b3b16c793 100644 --- a/docs/resources/changelog.md +++ b/docs/resources/changelog.md @@ -6,6 +6,13 @@ Components with the Experimental badge _During the beta period, these restrictions may be relaxed in the event of a mission-critical bug._ 🐛 +## 2.0.0-beta.56 + +This release is the second attempt at unbundling dependencies. This will be a breaking change only if your configuration _does not_ support bare module specifiers. CDN users and bundler users will be unaffected, but note the URLs for modules on the CDN must have the `/+esm` now. + +- Added the `hoist` attribute to `` [#564](https://github.com/shoelace-style/shoelace/issues/564) +- Unbundled dependencies and configured external imports to be packaged with bare module specifiers + ## 2.0.0-beta.55 - Revert unbundling due to issues with the CDN not handling bare module specifiers as expected @@ -17,7 +24,7 @@ Shoelace doesn't have a lot of dependencies, but this release unbundles most of - 🚨 BREAKING: renamed the `sl-clear` event to `sl-remove`, the `clear-button` part to `remove-button`, and the `clearable` property to `removable` in `` - Added the `disabled` prop to `` - Fixed a bug in `` where setting `disabled` initially didn't work -- Unbundled dependencies and configure external imports to use bare module specifiers +- Unbundled dependencies and configured external imports to be packaged with bare module specifiers ## 2.0.0-beta.53 diff --git a/package.json b/package.json index 8e5b4ecb3..49eb7e483 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "license": "MIT", "main": "dist/shoelace.js", "module": "dist/shoelace.js", - "customElements": "dist/custom-elements.json", + "customElements": "docs/dist/custom-elements.json", "type": "module", "types": "dist/shoelace.d.ts", "files": [ @@ -30,9 +30,10 @@ "url": "https://github.com/sponsors/claviska" }, "scripts": { - "start": "node scripts/build.js --dev", - "build": "node scripts/build.js", - "prepublishOnly": "npm run build && npm run test", + "start": "node scripts/build.js --dir docs/dist --bundle --serve", + "build": "node scripts/build.js --types", + "build-docs": "node scripts/build.js --dir docs/dist --bundle", + "prepublishOnly": "npm run build && npm run build-docs && npm run test", "prettier": "prettier --write --loglevel warn .", "create": "plop --plopfile scripts/plop/plopfile.cjs", "test": "web-test-runner \"src/**/*.test.ts\" --node-resolve --puppeteer", diff --git a/scripts/build.js b/scripts/build.js index 4933d276d..c5e0c2d32 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -7,34 +7,47 @@ import esbuild from 'esbuild'; import fs from 'fs'; import getPort from 'get-port'; import glob from 'globby'; +import mkdirp from 'mkdirp'; import path from 'path'; +import { URL } from 'url'; import { execSync } from 'child_process'; const build = esbuild.build; const bs = browserSync.create(); -const { dev } = commandLineArgs({ name: 'dev', type: Boolean }); -del.sync(['./dist', './docs/dist']); +const { bundle, dir, serve, types } = commandLineArgs([ + { name: 'dir', type: String, defaultValue: 'dist' }, + { name: 'serve', type: Boolean }, + { name: 'bundle', type: Boolean }, + { name: 'types', type: Boolean } +]); -try { - if (!dev) execSync('tsc', { stdio: 'inherit' }); // for type declarations - execSync('node scripts/make-metadata.js', { stdio: 'inherit' }); - execSync('node scripts/make-search.js', { stdio: 'inherit' }); - execSync('node scripts/make-vscode-data.js', { stdio: 'inherit' }); - execSync('node scripts/make-css.js', { stdio: 'inherit' }); - execSync('node scripts/make-icons.js', { stdio: 'inherit' }); -} catch (err) { - console.error(chalk.red(err)); - process.exit(1); -} +const __dirname = new URL('.', import.meta.url).pathname; +const rootDir = path.dirname(__dirname); +const outdir = path.relative(rootDir, dir); + +del.sync(outdir); +mkdirp.sync(outdir); (async () => { + try { + if (types) execSync(`tsc --project . --outdir "${outdir}"`, { stdio: 'inherit' }); + execSync(`node scripts/make-metadata.js --outdir "${outdir}"`, { stdio: 'inherit' }); + execSync(`node scripts/make-search.js --outdir "${outdir}"`, { stdio: 'inherit' }); + execSync(`node scripts/make-vscode-data.js --outdir "${outdir}"`, { stdio: 'inherit' }); + execSync(`node scripts/make-css.js --outdir "${outdir}"`, { stdio: 'inherit' }); + execSync(`node scripts/make-icons.js --outdir "${outdir}"`, { stdio: 'inherit' }); + } catch (err) { + console.error(chalk.red(err)); + process.exit(1); + } + const buildResult = await esbuild .build({ format: 'esm', target: 'es2017', entryPoints: [ - // The whole shebang dist + // The whole shebang './src/shoelace.ts', // Components ...(await glob('./src/components/**/!(*.(style|test)).ts')), @@ -43,14 +56,23 @@ try { // Theme stylesheets ...(await glob('./src/themes/**/!(*.test).ts')) ], - outdir: './dist', + outdir, chunkNames: 'chunks/[name].[hash]', - incremental: dev, + incremental: serve, define: { // Popper.js expects this to be set 'process.env.NODE_ENV': '"production"' }, bundle: true, + // + // We don't bundle certain dependencies in the production build. This ensures the dist ships with bare module + // specifiers, allowing end users to optimize better. jsDelivr understands this if you add /+esm to the URL. Note + // that we can't bundle packages that don't ship ESM. https://github.com/jsdelivr/jsdelivr/issues/18263 + // + // We still bundle for the dev environment and the docs build since we don't use a CDN for those. Once import maps + // are better supported, we can adjust for that and use the same build again. https://caniuse.com/import-maps + // + external: bundle ? undefined : ['@popperjs/core', '@shoelace-style/animations', 'lit', 'qr-creator'], splitting: true, plugins: [] }) @@ -59,17 +81,10 @@ try { process.exit(1); }); - // Create the docs distribution by copying dist into the docs folder. This is what powers the website. It doesn't need - // to exist in dev because Browser Sync routes it virtually. - if (!dev) { - await del('./docs/dist'); - await Promise.all([copy('./dist', './docs/dist')]); - } - - console.log(chalk.green('The build has finished! 📦\n')); + console.log(chalk.green(`The build has been generated at ${outdir} 📦\n`)); // Dev server - if (dev) { + if (serve) { const port = await getPort({ port: getPort.makeRange(4000, 4999) }); @@ -87,10 +102,7 @@ try { single: true, ghostMode: false, server: { - baseDir: 'docs', - routes: { - '/dist': './dist' - } + baseDir: 'docs' } }); @@ -103,7 +115,7 @@ try { .then(async () => { // Rebuild stylesheets when a theme file changes if (/^src\/themes/.test(filename)) { - execSync('node scripts/make-css.js', { stdio: 'inherit' }); + execSync(`node scripts/make-css.js --outdir "${outdir}"`, { stdio: 'inherit' }); } }) .then(() => { @@ -112,7 +124,7 @@ try { return; } - execSync('node scripts/make-metadata.js', { stdio: 'inherit' }); + execSync(`node scripts/make-metadata.js --outdir "${outdir}"`, { stdio: 'inherit' }); }) .then(() => bs.reload()) .catch(err => console.error(chalk.red(err))); @@ -121,7 +133,7 @@ try { // Reload without rebuilding when the docs change bs.watch(['docs/**/*.md']).on('change', filename => { console.log(`Docs file changed - ${filename}`); - execSync('node scripts/make-search.js', { stdio: 'inherit' }); + execSync(`node scripts/make-search.js --outdir "${outdir}"`, { stdio: 'inherit' }); bs.reload(); }); } diff --git a/scripts/make-css.js b/scripts/make-css.js index 12648d832..71440b13e 100644 --- a/scripts/make-css.js +++ b/scripts/make-css.js @@ -2,6 +2,7 @@ // This script generates stylesheets from all *.styles.ts files in src/themes // import chalk from 'chalk'; +import commandLineArgs from 'command-line-args'; import esbuild from 'esbuild'; import fs from 'fs/promises'; import glob from 'globby'; @@ -10,12 +11,13 @@ import path from 'path'; import prettier from 'prettier'; import stripComments from 'strip-css-comments'; +const { outdir } = commandLineArgs({ name: 'outdir', type: String }); const files = glob.sync('./src/themes/**/*.styles.ts'); -const outdir = './dist/themes'; +const themesDir = path.join(outdir, 'themes'); console.log('Generating stylesheets'); -mkdirp.sync(outdir); +mkdirp.sync(themesDir); try { files.map(async file => { @@ -32,7 +34,7 @@ try { const formattedStyles = prettier.format(stripComments(css), { parser: 'css' }); const filename = path.basename(file).replace('.styles.ts', '.css'); - const outfile = path.join(outdir, filename); + const outfile = path.join(themesDir, filename); await fs.writeFile(outfile, formattedStyles, 'utf8'); }); } catch (err) { diff --git a/scripts/make-icons.js b/scripts/make-icons.js index b7af4c833..54d462303 100644 --- a/scripts/make-icons.js +++ b/scripts/make-icons.js @@ -3,6 +3,7 @@ // import Promise from 'bluebird'; import chalk from 'chalk'; +import commandLineArgs from 'command-line-args'; import copy from 'recursive-copy'; import del from 'del'; import download from 'download'; @@ -13,7 +14,9 @@ import { stat, readFile, writeFile } from 'fs/promises'; import glob from 'globby'; import path from 'path'; -const iconDir = './dist/assets/icons'; +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; diff --git a/scripts/make-metadata.js b/scripts/make-metadata.js index 318967ecb..88d2f8ed0 100644 --- a/scripts/make-metadata.js +++ b/scripts/make-metadata.js @@ -2,11 +2,11 @@ // This script runs the Custom Elements Manifest analyzer to generate custom-elements.json // import chalk from 'chalk'; -import mkdirp from 'mkdirp'; +import commandLineArgs from 'command-line-args'; import { execSync } from 'child_process'; -mkdirp.sync('./dist'); +const { outdir } = commandLineArgs({ name: 'outdir', type: String }); // Run the analyzer console.log('Generating component metadata'); -execSync('cem analyze --litelement --outdir dist', { stdio: 'inherit' }); +execSync(`cem analyze --litelement --outdir "${outdir}"`, { stdio: 'inherit' }); diff --git a/scripts/make-search.js b/scripts/make-search.js index 8a92209b4..edaac0eda 100644 --- a/scripts/make-search.js +++ b/scripts/make-search.js @@ -1,10 +1,12 @@ +import commandLineArgs from 'command-line-args'; import fs from 'fs'; import path from 'path'; import glob from 'globby'; import lunr from 'lunr'; import { getAllComponents } from './shared.js'; -const metadata = JSON.parse(fs.readFileSync('./dist/custom-elements.json', 'utf8')); +const { outdir } = commandLineArgs({ name: 'outdir', type: String }); +const metadata = JSON.parse(fs.readFileSync(path.join(outdir, 'custom-elements.json'), 'utf8')); console.log('Generating search index for documentation'); diff --git a/scripts/make-vscode-data.js b/scripts/make-vscode-data.js index 317d19f17..2e6363686 100644 --- a/scripts/make-vscode-data.js +++ b/scripts/make-vscode-data.js @@ -4,10 +4,13 @@ // You must generate dist/custom-elements.json before running this script. // import chalk from 'chalk'; +import commandLineArgs from 'command-line-args'; import fs from 'fs'; +import path from 'path'; import { getAllComponents } from './shared.js'; -const metadata = JSON.parse(fs.readFileSync('./dist/custom-elements.json', 'utf8')); +const { outdir } = commandLineArgs({ name: 'outdir', type: String }); +const metadata = JSON.parse(fs.readFileSync(path.join(outdir, 'custom-elements.json'), 'utf8')); console.log('Generating custom data for VS Code'); @@ -53,4 +56,4 @@ components.map(component => { vscode.tags.push({ name, attributes }); }); -fs.writeFileSync('./dist/vscode.html-custom-data.json', JSON.stringify(vscode, null, 2), 'utf8'); +fs.writeFileSync(path.join(outdir, 'vscode.html-custom-data.json'), JSON.stringify(vscode, null, 2), 'utf8');