diff --git a/ember-scoped-css/index.js b/ember-scoped-css/index.js index b6f3f7f4..469b8079 100644 --- a/ember-scoped-css/index.js +++ b/ember-scoped-css/index.js @@ -1,17 +1,11 @@ -import addonJsUnplugin from './src/addon-js-unplugin.js'; -import addonCssRollup from './src/addon-css-rollup.js'; -import addonHbsRollup from './src/addon-hbs-rollup.js'; import appJsUnplugin from './src/app-js-unplugin.js'; import appCssUnplugin from './src/app-css-unplugin.js'; import appScopedcssWebpack from './src/app-scopedcss-webpack.js'; -import addonRewritecssRollup from './src/addon-rewritecss-rollup.js'; +import scopedCssUnplugin from './src/scoped-css-unplugin.js'; export { - addonJsUnplugin, - addonCssRollup, - addonHbsRollup, + scopedCssUnplugin, appJsUnplugin, appCssUnplugin, appScopedcssWebpack, - addonRewritecssRollup, }; diff --git a/ember-scoped-css/src/addon-css-rollup.js b/ember-scoped-css/src/addon-css-rollup.js deleted file mode 100644 index 85bd7926..00000000 --- a/ember-scoped-css/src/addon-css-rollup.js +++ /dev/null @@ -1,86 +0,0 @@ -import path from 'path'; -import { readFile } from 'fs/promises'; - -export default function () { - return { - name: 'addon-css-rollup', - - async resolveId(source, importer, options) { - // catch emited css files - if (source.endsWith('.css')) { - const resolution = await this.resolve(source, importer, { - ...options, - skipSelf: true, - }); - - if (resolution) { - return resolution; - } else { - const gjsCss = this.getModuleInfo(importer)?.meta?.gjsCss; - if (gjsCss) { - return { - external: false, - id: importer.replace(/\.js$/, '.css'), - meta: { - importer, - gjsCss, - }, - }; - } - } - } - return null; - }, - - async load(id) { - if (id.endsWith('.css')) { - const gjsCss = this.getModuleInfo(id).meta.gjsCss; - let css = gjsCss; - if (!css) { - css = await readFile(id, 'utf-8'); - } - - return css; - } - }, - - async transform(code, id) { - if (id.endsWith('.css')) { - this.emitFile({ - type: 'asset', - fileName: id.replace(path.join(process.cwd(), 'src/'), ''), - source: code, - }); - return ''; - } - }, - - generateBundle(a, bundle) { - let cssFiles = []; - for (let asset in bundle) { - const cssAsset = asset.replace('js', 'css'); - if (!asset.endsWith('js') || !bundle[cssAsset]) { - continue; - } - - if (process.env.environment === 'development') { - cssFiles.push(bundle[cssAsset].source); - delete bundle[cssAsset]; - } else { - // add import to js files - bundle[asset].code = - `import './${path.basename(asset.replace('.js', '.css'))}';\n` + - bundle[asset].code; - } - } - - if (process.env.environment === 'development') { - this.emitFile({ - type: 'asset', - fileName: 'scoped.css', - source: cssFiles.join('\n'), - }); - } - }, - }; -} diff --git a/ember-scoped-css/src/addon-hbs-rollup.js b/ember-scoped-css/src/addon-hbs-rollup.js deleted file mode 100644 index 203b8fa9..00000000 --- a/ember-scoped-css/src/addon-hbs-rollup.js +++ /dev/null @@ -1,46 +0,0 @@ -import { readFile } from 'fs/promises'; -import getPostfix from './lib/getPostfix.js'; -import replaceHbsInJs from './lib/replaceHbsInJs.js'; -import getClassesTagsFromCss from './lib/getClassesTagsFromCss.js'; -import rewriteHbs from './lib/rewriteHbs.js'; -import fsExists from './lib/fsExists.js'; - -export default function rollupCssColocation() { - return { - name: 'addon-hbs-rollup', - - async transform(code, id) { - if (id.endsWith('.hbs.js')) { - const hbsPath = id.replace('.js', ''); - const cssPath = hbsPath.replace('.hbs', '.css'); - - const cssExists = await fsExists(cssPath); - if (cssExists) { - // read the css file - // TODO: get css from loader, because there are classes in imported css files; css can be stored in meta!!!!! - // const resolution = await this.resolve(importPath, id); - // resolution.meta.internalImport = true; - // const importedCss = await this.load(resolution); - const css = await readFile(cssPath, 'utf-8'); - const { classes, tags } = getClassesTagsFromCss(css); - - // generate unique postfix - const postfix = getPostfix(cssPath); - - // rewrite the template - const rewrittenHbsJs = replaceHbsInJs(code, (hbs) => { - // add dependency to the css file - this.addWatchFile(cssPath); - return rewriteHbs(hbs, classes, tags, postfix); - }); - - return { - code: rewrittenHbsJs, - // this rollup plugin changes only the template string, so the code structure is the same - map: null, - }; - } - } - }, - }; -} diff --git a/ember-scoped-css/src/addon-js-unplugin.js b/ember-scoped-css/src/addon-js-unplugin.js deleted file mode 100644 index 95634561..00000000 --- a/ember-scoped-css/src/addon-js-unplugin.js +++ /dev/null @@ -1,65 +0,0 @@ -import { createUnplugin } from 'unplugin'; -import { readFile } from 'fs/promises'; -import path from 'path'; -import getClassesTagsFromCss from './lib/getClassesTagsFromCss.js'; -import getPostfix from './lib/getPostfix.js'; -import replaceHbsInJs from './lib/replaceHbsInJs.js'; -import rewriteHbs from './lib/rewriteHbs.js'; -import fsExists from './lib/fsExists.js'; -import findCssInJs from './lib/findCssInJs.js'; -import recast from 'recast'; - -export default createUnplugin(() => { - return { - name: 'addon-js-unplugin', - - transformInclude(id) { - return id.endsWith('.js') || id.endsWith('.ts'); - }, - - async transform(code, jsPath) { - const cssPath = jsPath.replace(/(\.hbs)?\.((js)|(ts))$/, '.css'); - const cssFileName = path.basename(cssPath); - - const cssExists = await fsExists(cssPath); - let css; - if (cssExists) { - css = await readFile(cssPath, 'utf8'); - } else { - if (code.includes('__GLIMMER_STYLES')) { - const result = findCssInJs(code, true); - css = result.css; - code = recast.print(result.ast).code; - this.getModuleInfo(jsPath).meta.gjsCss = result.css; - - // TODO: generate changed source map. Implementation depends on implemented rollup plugin for style tag - } - } - - if (!css) { - return { - code, - map: null, - }; - } - - // add css import for js and gjs files - code = `import './${cssFileName}';\n\n${code}`; - - // rewrite hbs in js in case it is gjs file (for gjs files hbs is already in js file) - // for js components "@embroider/addon-dev/template-colocation-plugin", will add hbs to js later. So there is hbs plugin to rewrite hbs - - const rewrittenCode = replaceHbsInJs(code, (hbs) => { - const { classes, tags } = getClassesTagsFromCss(css); - const postfix = getPostfix(cssPath); - const rewritten = rewriteHbs(hbs, classes, tags, postfix); - return rewritten; - }); - - return { - code: rewrittenCode, - map: null, - }; - }, - }; -}); diff --git a/ember-scoped-css/src/addon-rewritecss-rollup.js b/ember-scoped-css/src/addon-rewritecss-rollup.js deleted file mode 100644 index 154d8c84..00000000 --- a/ember-scoped-css/src/addon-rewritecss-rollup.js +++ /dev/null @@ -1,31 +0,0 @@ -import path from 'path'; -import getPostfix from './lib/getPostfix.js'; -import rewriteCss from './lib/rewriteCss.js'; -import fsExists from './lib/fsExists.js'; - -export default function () { - return { - name: 'addon-rewritecss-rollup', - - async transform(code, id) { - if (!id.endsWith('.css')) { - return; - } - const postfix = getPostfix(id); - const jsPath = id.replace(/\.css$/, '.gjs'); - const gtsPath = id.replace(/\.css$/, '.gts'); - const hbsPath = id.replace(/\.css$/, '.hbs'); - - const [jsExists, gtsExists, hbsExists] = await Promise.all([ - fsExists(jsPath), - fsExists(gtsPath), - fsExists(hbsPath), - ]); - - if (jsExists || hbsExists || gtsExists) { - const rewritten = rewriteCss(code, postfix, path.basename(id)); - return rewritten; - } - }, - }; -} diff --git a/ember-scoped-css/src/app-css-livereload-loader.js b/ember-scoped-css/src/app-css-livereload-loader.js index a493e737..4a29cee6 100644 --- a/ember-scoped-css/src/app-css-livereload-loader.js +++ b/ember-scoped-css/src/app-css-livereload-loader.js @@ -2,7 +2,7 @@ import { createUnplugin } from 'unplugin'; import path from 'path'; import { readFile } from 'fs/promises'; import { Compilation } from 'webpack'; -import getPostfix from './lib/getPostfix.js'; +import generateHash from './lib/generateAbsolutePathHash.js'; export default createUnplugin(({ loaders, htmlEntrypointInfo }) => { return { @@ -41,7 +41,7 @@ export default createUnplugin(({ loaders, htmlEntrypointInfo }) => { css = await loader.bind({ resourcePath: cssPath })(css); } // random string; lenght is 8 - const postfix = getPostfix(path.basename(cssPath)); + const postfix = generateHash(path.basename(cssPath)); this.emitFile({ type: 'asset', diff --git a/ember-scoped-css/src/app-css-loader.js b/ember-scoped-css/src/app-css-loader.js index adce28fa..0756b71c 100644 --- a/ember-scoped-css/src/app-css-loader.js +++ b/ember-scoped-css/src/app-css-loader.js @@ -1,14 +1,14 @@ // import { createUnplugin } from 'unplugin'; import { basename } from 'path'; import fsExists from './lib/fsExists.js'; -import getPostfix from './lib/getPostfix.js'; +import generateHash from './lib/generateAbsolutePathHash.js'; import rewriteCss from './lib/rewriteCss.js'; // import path from 'path'; export default async function (code) { const cssPath = this.resourcePath; const cssFileName = basename(cssPath); - const postfix = getPostfix(cssPath); + const postfix = generateHash(cssPath); const hbsPath = cssPath.replace('.css', '.hbs'); const gjsPath = cssPath.replace('.css', '.js'); diff --git a/ember-scoped-css/src/app-js-unplugin.js b/ember-scoped-css/src/app-js-unplugin.js index 6f890830..09d121b9 100644 --- a/ember-scoped-css/src/app-js-unplugin.js +++ b/ember-scoped-css/src/app-js-unplugin.js @@ -1,7 +1,7 @@ import { createUnplugin } from 'unplugin'; import replaceGlimmerAst from './lib/replaceGlimmerAst.js'; import path from 'path'; -import getPostfix from './lib/getPostfix.js'; +import generateHash from './lib/generateAbsolutePathHash.js'; import getClassesTagsFromCss from './lib/getClassesTagsFromCss.js'; function* iterateOpcodes(opcodes) { @@ -48,7 +48,7 @@ export default createUnplugin(({ appDir }) => { async transform(code, id) { const cssPath = id.replace(/(\.js)|(\.hbs)/, '.css'); - const postfix = getPostfix(cssPath); + const postfix = generateHash(cssPath); return await replaceGlimmerAst(code, id, (opcodes, css) => { const { classes, tags } = getClassesTagsFromCss(css); diff --git a/ember-scoped-css/src/app-scopedcss-webpack.js b/ember-scoped-css/src/app-scopedcss-webpack.js index 53a71916..1386dc23 100644 --- a/ember-scoped-css/src/app-scopedcss-webpack.js +++ b/ember-scoped-css/src/app-scopedcss-webpack.js @@ -2,7 +2,7 @@ import rewriteCss from './lib/rewriteCss.js'; import { readFile, writeFile } from 'fs/promises'; import path from 'path'; -import getPostfix from './lib/getPostfix.js'; +import generateHash from './lib/generateAbsolutePathHash.js'; import fsExists from './lib/fsExists.js'; import getFiles from './lib/getFiles.js'; @@ -44,7 +44,7 @@ export default class { continue; } const fileName = path.basename(file); - const postfix = getPostfix(fileName); + const postfix = generateHash(fileName); const css = await readFile(file, 'utf-8'); const rewrittenCss = rewriteCss(css, postfix, fileName); diff --git a/ember-scoped-css/src/lib/getPostfix.js b/ember-scoped-css/src/lib/generateAbsolutePathHash.js similarity index 74% rename from ember-scoped-css/src/lib/getPostfix.js rename to ember-scoped-css/src/lib/generateAbsolutePathHash.js index 2450e4d1..83e0765d 100644 --- a/ember-scoped-css/src/lib/getPostfix.js +++ b/ember-scoped-css/src/lib/generateAbsolutePathHash.js @@ -1,4 +1,4 @@ -import generateHash from './generateHash.js'; +import generateHash from './generateRelativePathHash.js'; import path from 'path'; export default function (cssFileName) { diff --git a/ember-scoped-css/src/lib/generateHash.js b/ember-scoped-css/src/lib/generateRelativePathHash.js similarity index 100% rename from ember-scoped-css/src/lib/generateHash.js rename to ember-scoped-css/src/lib/generateRelativePathHash.js diff --git a/ember-scoped-css/src/lib/replaceScopedClass.js b/ember-scoped-css/src/lib/replaceScopedClass.js index e16cfbce..2d804929 100644 --- a/ember-scoped-css/src/lib/replaceScopedClass.js +++ b/ember-scoped-css/src/lib/replaceScopedClass.js @@ -1,12 +1,12 @@ import recast from 'ember-template-recast'; import renameClass from './renameClass.js'; -import getPostfix from './getPostfix.js'; +import generateHash from './generateAbsolutePathHash.js'; export default function (hbs, templatePath) { let ast = recast.parse(hbs); let stack = []; const cssPath = templatePath.replace(/(\.hbs)?\.js$/, '.css'); - const postfix = getPostfix(cssPath); + const postfix = generateHash(cssPath); recast.traverse(ast, { All: { diff --git a/ember-scoped-css/src/scoped-css-unplugin.js b/ember-scoped-css/src/scoped-css-unplugin.js new file mode 100644 index 00000000..fc8c64b1 --- /dev/null +++ b/ember-scoped-css/src/scoped-css-unplugin.js @@ -0,0 +1,116 @@ +import { createUnplugin } from 'unplugin'; +import { readFile } from 'fs/promises'; +import path from 'path'; +import getClassesTagsFromCss from './lib/getClassesTagsFromCss.js'; +import generateHash from './lib/generateAbsolutePathHash.js'; +import replaceHbsInJs from './lib/replaceHbsInJs.js'; +import rewriteHbs from './lib/rewriteHbs.js'; +import fsExists from './lib/fsExists.js'; +import rewriteCss from './lib/rewriteCss.js'; + +function isJsFile(id) { + return id.endsWith('.js') || id.endsWith('.ts'); +} + +function isCssFile(id) { + return id.endsWith('.css'); +} + +async function transformJsFile(code, jsPath) { + const cssPath = jsPath.replace(/(\.hbs)?\.((js)|(ts))$/, '.css'); + const cssFileName = path.basename(cssPath); + + const cssExists = await fsExists(cssPath); + let css; + if (cssExists) { + css = await readFile(cssPath, 'utf8'); + } else { + return { + code, + map: null, + }; + } + + // add css import for js and gjs files + code = `import './${cssFileName}';\n\n${code}`; + + // rewrite hbs in js in case it is gjs file (for gjs files hbs is already in js file) + + const rewrittenCode = replaceHbsInJs(code, (hbs) => { + const { classes, tags } = getClassesTagsFromCss(css); + const postfix = generateHash(cssPath); + const rewritten = rewriteHbs(hbs, classes, tags, postfix); + return rewritten; + }); + + return { + code: rewrittenCode, + map: null, + }; +} + +async function transformCssFile(code, id, emitFile) { + const jsPath = id.replace(/\.css$/, '.gjs'); + const gtsPath = id.replace(/\.css$/, '.gts'); + const hbsPath = id.replace(/\.css$/, '.hbs'); + + const [jsExists, gtsExists, hbsExists] = await Promise.all([ + fsExists(jsPath), + fsExists(gtsPath), + fsExists(hbsPath), + ]); + + if (jsExists || hbsExists || gtsExists) { + const postfix = generateHash(id); + code = rewriteCss(code, postfix, path.basename(id)); + } + + emitFile({ + type: 'asset', + fileName: id.replace(path.join(process.cwd(), 'src/'), ''), + source: code, + }); + return ''; +} + +export default createUnplugin(() => { + return { + name: 'ember-scoped-css-unplugin', + + generateBundle(a, bundle) { + let cssFiles = []; + for (let asset in bundle) { + const cssAsset = asset.replace('js', 'css'); + if (!asset.endsWith('js') || !bundle[cssAsset]) { + continue; + } + + if (process.env.environment === 'development') { + cssFiles.push(bundle[cssAsset].source); + delete bundle[cssAsset]; + } else { + // add import to js files + bundle[asset].code = + `import './${path.basename(asset.replace('.js', '.css'))}';\n` + + bundle[asset].code; + } + } + + if (process.env.environment === 'development') { + this.emitFile({ + type: 'asset', + fileName: 'scoped.css', + source: cssFiles.join('\n'), + }); + } + }, + + transform(code, jsPath) { + if (isJsFile(jsPath)) { + return transformJsFile(code, jsPath); + } else if (isCssFile(jsPath)) { + return transformCssFile(code, jsPath, this.emitFile); + } + }, + }; +}); diff --git a/ember-scoped-css/src/scopedClass.js b/ember-scoped-css/src/scopedClass.js index d5dcf15f..39503b17 100644 --- a/ember-scoped-css/src/scopedClass.js +++ b/ember-scoped-css/src/scopedClass.js @@ -1,4 +1,4 @@ -import generateHash from './lib/generateHash.js'; +import generateHash from './lib/generateRelativePathHash.js'; import renameClass from './lib/renameClass.js'; export function scopedClass(className, relativeCssPath) { diff --git a/ember-scoped-css/test/getPostfix.js b/ember-scoped-css/test/generateHash.js similarity index 58% rename from ember-scoped-css/test/getPostfix.js rename to ember-scoped-css/test/generateHash.js index cd28f881..1726e05a 100644 --- a/ember-scoped-css/test/getPostfix.js +++ b/ember-scoped-css/test/generateHash.js @@ -1,21 +1,21 @@ import { expect } from 'chai'; -import getPostfix from '../src/lib/getPostfix.js'; +import generateHash from '../src/lib/generateAbsolutePathHash.js'; -describe('getPostfix', function () { +describe('generateHash', function () { it('should return a string', function () { - const postfix = getPostfix('foo.css'); + const postfix = generateHash('foo.css'); expect(postfix).to.be.a('string'); }); it('should return a string starting with "e"', function () { - const postfix = getPostfix('foo.css'); + const postfix = generateHash('foo.css'); expect(postfix).to.match(/^e/); }); it('should return a string of length 9', function () { - const postfix = getPostfix('foo.css'); + const postfix = generateHash('foo.css'); expect(postfix).to.have.lengthOf(9); }); diff --git a/test-apps/v2-addon/fixtures/expected-dist/components/alert.js b/test-apps/v2-addon/fixtures/expected-dist/components/alert.js index dc47e651..7c56e05b 100644 --- a/test-apps/v2-addon/fixtures/expected-dist/components/alert.js +++ b/test-apps/v2-addon/fixtures/expected-dist/components/alert.js @@ -3,7 +3,7 @@ import { setComponentTemplate } from '@ember/component'; import { precompileTemplate } from '@ember/template-compilation'; import templateOnly from '@ember/component/template-only'; -var TEMPLATE = precompileTemplate("
\n

\n {{@title}}\n

\n

\n {{@message}}\n

\n
"); +var TEMPLATE = precompileTemplate("
\n

\n {{@title}}\n

\n

\n {{@message}}\n

\n
"); var alert = setComponentTemplate(TEMPLATE, templateOnly()); diff --git a/test-apps/v2-addon/rollup.config.mjs b/test-apps/v2-addon/rollup.config.mjs index fa717ee6..a10fc701 100644 --- a/test-apps/v2-addon/rollup.config.mjs +++ b/test-apps/v2-addon/rollup.config.mjs @@ -2,12 +2,7 @@ import { babel } from '@rollup/plugin-babel'; import copy from 'rollup-plugin-copy'; import { Addon } from '@embroider/addon-dev/rollup'; import { glimmerTemplateTag } from 'rollup-plugin-glimmer-template-tag'; -import { - addonCssRollup, - addonRewritecssRollup, - addonJsUnplugin, - addonHbsRollup, -} from 'ember-scoped-css'; +import { scopedCssUnplugin } from 'ember-scoped-css'; const addon = new Addon({ srcDir: 'src', @@ -54,10 +49,7 @@ export default { // addon.keepAssets(['**/*.css']), // eslint-disable-next-line no-undef // eslint-disable-next-line no-undef - addonRewritecssRollup(), - addonCssRollup(), - addonJsUnplugin.rollup(), - addonHbsRollup(), + scopedCssUnplugin.rollup(), // Remove leftover build artifacts when starting a new build. addon.clean(),