From 42962eaa171b24dc7df9800478b04bcc5798b403 Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Tue, 9 Apr 2024 16:31:37 +0100 Subject: [PATCH] add a namespace option for public-assets plugin --- docs/v2-faq.md | 3 +- packages/addon-dev/README.md | 4 + .../addon-dev/src/rollup-public-assets.ts | 30 +++++- packages/addon-dev/src/rollup.ts | 7 +- tests/scenarios/v2-addon-dev-test.ts | 101 ++++++++++++++++-- 5 files changed, 130 insertions(+), 15 deletions(-) diff --git a/docs/v2-faq.md b/docs/v2-faq.md index 542c5c3b9e..2865ae01d3 100644 --- a/docs/v2-faq.md +++ b/docs/v2-faq.md @@ -141,7 +141,8 @@ This is done by adding some meta data to the addon's `package.json`, specifying } ``` -If you have many files you want to expose this way, you can instead add the `addon.publicAssets()` plugin from `@embroider/addon-dev` to your Rollup config to automate the generation of this mapping data. +If you have many files you want to expose this way, you can instead add the `addon.publicAssets()` plugin from `@embroider/addon-dev` to your Rollup config to automate the generation of this mapping data. This rollup plugin will automatically prefix your public assets with a folder name that matches your addon packages name, this is to prevent any name clashes between addons. You can read more about it in the docs for the addon-dev rollup plugin utilities https://github.com/embroider-build/embroider/tree/main/packages/addon-dev#rollup-utilities + ## Build setup diff --git a/packages/addon-dev/README.md b/packages/addon-dev/README.md index e1aa7bc627..1c63cb3443 100644 --- a/packages/addon-dev/README.md +++ b/packages/addon-dev/README.md @@ -18,6 +18,10 @@ For a guide on porting a V1 addon to V2, see https://github.com/embroider-build/ 2. Copy the `./sample-rollup.config.js` in this repo to your own `rollup.config.js`. 3. Copy the `./sample-babel.config.json` in this repo to your own `babel.config.json`. +### addon.publicAssets(path , options) + +A rollup plugin to expose a folder of assets. `path` is a required to define which folder to expose. `options.include` is a glob pattern passed to `walkSync.include` to pick files. `options.exlude` is a glob pattern passed to `walkSync.ignore` to exclude files. `options.namespace` is the namespace to expose files, defaults to the package name + ## addon-dev command The `addon-dev` command helps with common tasks in v2 addons. diff --git a/packages/addon-dev/src/rollup-public-assets.ts b/packages/addon-dev/src/rollup-public-assets.ts index 156018524b..6e4a7f1a88 100644 --- a/packages/addon-dev/src/rollup-public-assets.ts +++ b/packages/addon-dev/src/rollup-public-assets.ts @@ -1,10 +1,34 @@ import { readJsonSync, writeJsonSync } from 'fs-extra'; import walkSync from 'walk-sync'; import type { Plugin } from 'rollup'; +import { resolve, join } from 'path'; +export interface PublicAssetsOptions { + /** + * glob pattern passed to `walkSync.include` to pick files + */ + include?: string[]; + + /** + * glob pattern passed to `walkSync.ignore` to exclude files + */ + exclude?: string[]; + + /** + * namespace to expose files + */ + namespace?: string; +} + +/** + * A rollup plugin to expose a folder of assets + * + * @param path - the public folder that you want to add as public assets + * @returns + */ export default function publicAssets( path: string, - opts: { include: string[]; exclude: string[] } + opts?: PublicAssetsOptions ): Plugin { const includeGlobPatterns = opts?.include; const excludedGlobPatterns = opts?.exclude || []; @@ -27,7 +51,9 @@ export default function publicAssets( }); const publicAssets: Record = filenames.reduce( (acc: Record, v): Record => { - acc[`./${path}/${v}`] = ['/', pkg.name, '/', path, '/', v].join(''); + acc[`./${path}/${v}`] = resolve( + '/' + join(opts?.namespace ?? pkg.name, path, v) + ); return acc; }, {} diff --git a/packages/addon-dev/src/rollup.ts b/packages/addon-dev/src/rollup.ts index f4056128f0..cb755e3b5b 100644 --- a/packages/addon-dev/src/rollup.ts +++ b/packages/addon-dev/src/rollup.ts @@ -6,7 +6,10 @@ import type { Options as DelOptions } from 'rollup-plugin-delete'; import { default as clean } from 'rollup-plugin-delete'; import { default as keepAssets } from './rollup-keep-assets'; import { default as dependencies } from './rollup-addon-dependencies'; -import { default as publicAssets } from './rollup-public-assets'; +import { + default as publicAssets, + type PublicAssetsOptions, +} from './rollup-public-assets'; import type { Plugin } from 'rollup'; export class Addon { @@ -102,7 +105,7 @@ export class Addon { return dependencies(); } - publicAssets(path: string, opts: { include: string[]; exclude: string[] }) { + publicAssets(path: string, opts?: PublicAssetsOptions) { return publicAssets(path, opts); } } diff --git a/tests/scenarios/v2-addon-dev-test.ts b/tests/scenarios/v2-addon-dev-test.ts index c0ad046f8f..d4d49c226b 100644 --- a/tests/scenarios/v2-addon-dev-test.ts +++ b/tests/scenarios/v2-addon-dev-test.ts @@ -1,6 +1,6 @@ import path from 'path'; import { appScenarios, baseV2Addon } from './scenarios'; -import { PreparedApp } from 'scenario-tester'; +import { PreparedApp, type Project } from 'scenario-tester'; import QUnit from 'qunit'; import merge from 'lodash/merge'; import type { ExpectFile } from '@embroider/test-support/file-assertions/qunit'; @@ -74,6 +74,7 @@ appScenarios addon.hbs(), addon.gjs(), addon.dependencies(), + addon.publicAssets('public'), babel({ babelHelpers: 'bundled', extensions: ['.js', '.hbs', '.gjs'] }), @@ -151,19 +152,87 @@ appScenarios }, }, }, + public: { + 'thing.txt': 'hello there', + }, }); - addon.linkDependency('@embroider/addon-shim', { baseDir: __dirname }); - addon.linkDependency('@embroider/addon-dev', { baseDir: __dirname }); - addon.linkDependency('babel-plugin-ember-template-compilation', { baseDir: __dirname }); - addon.linkDevDependency('@babel/core', { baseDir: __dirname }); - addon.linkDevDependency('@babel/plugin-transform-class-static-block', { baseDir: __dirname }); - addon.linkDevDependency('@babel/plugin-transform-class-properties', { baseDir: __dirname }); - addon.linkDevDependency('@babel/plugin-proposal-decorators', { baseDir: __dirname }); - addon.linkDevDependency('@rollup/plugin-babel', { baseDir: __dirname }); - addon.linkDevDependency('rollup', { baseDir: __dirname }); + let addonNoNamespace = baseV2Addon(); + addonNoNamespace.pkg.name = 'v2-addon-no-namespace'; + addonNoNamespace.pkg.scripts = { + build: 'node ./node_modules/rollup/dist/bin/rollup -c ./rollup.config.mjs', + }; + + merge(addonNoNamespace.files, { + src: { + components: { + 'hello.hbs': '

hello

', + }, + }, + public: { + 'other.txt': 'we meet again', + }, + 'babel.config.json': ` + { + "plugins": [ + "@embroider/addon-dev/template-colocation-plugin", + "@babel/plugin-transform-class-static-block", + ["babel-plugin-ember-template-compilation", { + targetFormat: 'hbs', + compilerPath: 'ember-source/dist/ember-template-compiler', + }], + ["@babel/plugin-proposal-decorators", { "legacy": true }], + [ "@babel/plugin-transform-class-properties" ] + ] + } + `, + 'rollup.config.mjs': ` + import { babel } from '@rollup/plugin-babel'; + import { Addon } from '@embroider/addon-dev/rollup'; + + const addon = new Addon({ + srcDir: 'src', + destDir: 'dist', + }); + + export default { + output: addon.output(), + + plugins: [ + addon.publicEntrypoints([ + 'components/**/*.js', + ], { + exclude: ['**/-excluded/**/*'], + }), + + addon.hbs(), + addon.publicAssets('public', { namespace: '' }), + + babel({ babelHelpers: 'bundled', extensions: ['.js', '.hbs', '.gjs'] }), + + addon.clean(), + ], + }; + `, + }); + + function linkDependencies(addon: Project) { + addon.linkDependency('@embroider/addon-shim', { baseDir: __dirname }); + addon.linkDependency('@embroider/addon-dev', { baseDir: __dirname }); + addon.linkDependency('babel-plugin-ember-template-compilation', { baseDir: __dirname }); + addon.linkDevDependency('@babel/core', { baseDir: __dirname }); + addon.linkDevDependency('@babel/plugin-transform-class-static-block', { baseDir: __dirname }); + addon.linkDevDependency('@babel/plugin-transform-class-properties', { baseDir: __dirname }); + addon.linkDevDependency('@babel/plugin-proposal-decorators', { baseDir: __dirname }); + addon.linkDevDependency('@rollup/plugin-babel', { baseDir: __dirname }); + addon.linkDevDependency('rollup', { baseDir: __dirname }); + } + + linkDependencies(addon); + linkDependencies(addonNoNamespace); project.addDevDependency(addon); + project.addDependency(addonNoNamespace); merge(project.files, { tests: { @@ -226,6 +295,7 @@ appScenarios if (result.exitCode !== 0) { throw new Error(result.output); } + await inDependency(app, 'v2-addon-no-namespace').execute('pnpm build'); }); hooks.beforeEach(assert => { @@ -301,6 +371,17 @@ setComponentTemplate( export { SingleFileComponent as default }; //# sourceMappingURL=single-file-component.js.map`); }); + + test('publicAssets are namespaced correctly', async function (assert) { + let expectNoNamespaceFile = expectFilesAt(inDependency(app, 'v2-addon-no-namespace').dir, { qunit: assert }); + + expectFile('package.json').json('ember-addon.public-assets').deepEquals({ + './public/thing.txt': '/v2-addon/public/thing.txt', + }); + expectNoNamespaceFile('package.json').json('ember-addon.public-assets').deepEquals({ + './public/other.txt': '/public/other.txt', + }); + }); }); Qmodule('Consuming app', function () {