From 27496d7af5b55f218e74145a2d4dc659e017d92c Mon Sep 17 00:00:00 2001 From: Jesse Jurman Date: Sat, 13 Jan 2024 00:01:33 -0500 Subject: [PATCH] Exclude unused components in js export (v5.3.0) --- build.js | 57 +++++++++++++------------ docs/pages/importing-and-exporting.html | 9 +--- package-lock.json | 4 +- package.json | 2 +- src/scripts/export-script.js | 40 ++++++++++++++--- 5 files changed, 68 insertions(+), 44 deletions(-) diff --git a/build.js b/build.js index 8e4556e..a44ebf0 100644 --- a/build.js +++ b/build.js @@ -10,31 +10,32 @@ const UglifyJS = require('uglify-js'); const { version } = require('./package.json'); // before we start anything, make sure the output directory exists and is empty -if (!fs.existsSync('output')) { - // if it doesn't exist make it - fs.mkdirSync('output'); -} else { - // if it does, remove all files in the directory - const files = fs.readdirSync('output'); - for (const file of files) { - const filePath = path.join('output', file); - fs.unlinkSync(filePath); +const outputDirectories = ['output', 'output/export', 'output/export/processors']; +outputDirectories.forEach((directory) => { + if (!fs.existsSync(directory)) { + // if it doesn't exist make it + fs.mkdirSync(directory); + } else { + // if it does, remove all files in the directory + const files = fs.readdirSync(directory); + for (const file of files) { + const filePath = path.join(directory, file); + try { + fs.unlinkSync(filePath); + } catch { + // if we tried to delete a directory, this will blow up + // but it's actually fine... + } + } } -} +}); // load all source class files (these will be included in all builds) // ORDER IS IMPORTANT! We need ComponentDefinition to be last, so that // shadow root processors exist by the time we start processing templates -const classFiles = [ - 'src/TramLite.js', - 'src/processors/ComponentEffect.js', - 'src/processors/ControlledInput.js', - 'src/processors/AttrBroadcaster.js', - 'src/processors/EventRebroadcaster.js', - 'src/processors/ExternalScript.js', - 'src/processors/ContextConsumer.js', - 'src/ComponentDefinition.js', -]; +const processors = fs.readdirSync('src/processors').map((file) => `src/processors/${file}`); +const classFiles = ['src/TramLite.js', ...processors, 'src/ComponentDefinition.js']; + const loadedClassFiles = Object.fromEntries( classFiles.map((filePath) => { console.log('loading', filePath); @@ -67,11 +68,6 @@ const buildConfigs = [ defines: { INSTALL: false }, enclose: true, }, - { - outputFile: 'output/export-dependencies.js', - files: { ...loadedClassFiles, ...importComponentClass }, - defines: { INSTALL: false }, - }, ]; buildConfigs.forEach((config) => { @@ -97,12 +93,19 @@ buildConfigs.forEach((config) => { const minifyConfigs = [ { inputFile: 'output/tram-lite.js', outputFile: 'output/tram-lite.min.js' }, { inputFile: 'output/import-components.js', outputFile: 'output/import-components.min.js' }, - { inputFile: 'output/export-dependencies.js', outputFile: 'output/export-dependencies.min.js' }, + // build files for use with the export-components script + ...classFiles.map((classFile) => ({ + inputFile: classFile, + outputFile: classFile.replace('src', 'output/export').replace('.js', '.min.js'), + })), + { inputFile: 'src/ImportComponent.js', outputFile: 'output/export/ImportComponent.min.js' }, ]; minifyConfigs.forEach((config) => { console.log('minifying', config.outputFile); - const result = UglifyJS.minify(fs.readFileSync(config.inputFile, 'utf8')); + const result = UglifyJS.minify(fs.readFileSync(config.inputFile, 'utf8'), { + compress: { global_defs: { APP_VERSION: version, INSTALL: false } }, + }); fs.writeFileSync(config.outputFile, result.code); }); diff --git a/docs/pages/importing-and-exporting.html b/docs/pages/importing-and-exporting.html index 50b5e83..349b87c 100644 --- a/docs/pages/importing-and-exporting.html +++ b/docs/pages/importing-and-exporting.html @@ -74,22 +74,17 @@

Parameters

- Aside from the required components, the command has two optional flags, --output and - --minified. + Aside from the required components, the command has one optional flag, --output.

--output (or -o) can be used to set the file name and directory of the resulting javascript. If this flag is missing, the command will place the file in the current directory, named based on the component files passed in.

-

- --minified (or -m) can be used to import the minified Tram-Lite code as part of your export. - This should reduce the total size of the exported components. -

Example Exporting an HTML Template to Javascript

Similar to the example above, we start with a component definition in an x-button.html. diff --git a/package-lock.json b/package-lock.json index 437e0e6..d6ae858 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "tram-lite", - "version": "5.2.0", + "version": "5.3.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "tram-lite", - "version": "5.2.0", + "version": "5.3.0", "license": "MIT", "devDependencies": { "cypress": "^12.14.0", diff --git a/package.json b/package.json index c524f52..30ed195 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tram-lite", - "version": "5.2.0", + "version": "5.3.0", "description": "💡 HTML library for building and enhancing web-components", "homepage": "https://tram-one.io/tram-lite/", "repository": "https://github.com/Tram-One/tram-lite", diff --git a/src/scripts/export-script.js b/src/scripts/export-script.js index 9ae81e9..4ec03c9 100644 --- a/src/scripts/export-script.js +++ b/src/scripts/export-script.js @@ -7,9 +7,6 @@ const fs = require('fs'); const path = require('path'); -// check to see if we should use the minified flag for external dependencies -const useMinified = process.argv.includes('-m') || process.argv.includes('--minified'); - // check to see if there is a predefined output filename (otherwise we will try to generate one) const outputFlagIndex = process.argv.findIndex((arg) => arg === '-o' || arg === '--output'); const customOutputFile = outputFlagIndex !== -1 ? process.argv[outputFlagIndex + 1] : null; @@ -26,11 +23,40 @@ if (filePaths.length === 0) { console.log('processing', filePaths, 'for export'); const componentDefinitions = filePaths.map((filePath) => fs.readFileSync(filePath, 'utf8')); -const tramLiteExportDependenciesPath = useMinified - ? path.join(__dirname, './export-dependencies.min.js') - : path.join(__dirname, './export-dependencies.js'); +// load the core Tram-Lite library and classes (which are always needed) +const coreFiles = [ + './export/TramLite.min.js', + './export/ComponentDefinition.min.js', + './export/ImportComponent.min.js', +].map((filePath) => fs.readFileSync(path.join(__dirname, filePath)).toString()); + +// load all shadow root processors +const processorFiles = fs + .readdirSync(path.join(__dirname, './export/processors')) + .map((file) => `./export/processors/${file}`) + .map((filePath) => fs.readFileSync(path.join(__dirname, filePath)).toString()); + +// determine if we need these processors for these components +// (note, this is a very weak regex check, and not using proper CSS or static analysis) +const processorFilesToInclude = processorFiles.filter((processorContent) => { + // the following regex matches on calls to appendShadowRootProcessor + // it specifically captures the first parameter (a CSS selector), and + // parses out the parameter as a whole, and then the text inside `[...]` + const matches = processorContent.match(/TramLite\.appendShadowRootProcessor\(\"([^\[\,]*(\[?(.+)\])?[^\]\,]*)\"/); + + if (!matches) { + console.log(processorContent); + } + + // get the last most match (the most specific) + const keySelector = [matches[1], matches[2], matches[3]].filter((selector) => selector != undefined).at(-1); + + // see if any of our component definitions match with this selector + return componentDefinitions.some((template) => template.match(keySelector)); +}); + +const tramLiteExportDepenedencies = [...coreFiles, ...processorFilesToInclude].join(''); -const tramLiteExportDepenedencies = fs.readFileSync(tramLiteExportDependenciesPath).toString(); const templateAndLoadCode = componentDefinitions .map((componentCode) => { // update the component code, in case it also uses ``, we'll need to escape them