diff --git a/lib/css-plugin.js b/lib/css-plugin.js index 952da5fc..44d78243 100644 --- a/lib/css-plugin.js +++ b/lib/css-plugin.js @@ -19,7 +19,6 @@ import postcss from 'postcss'; import postCSSNested from 'postcss-nested'; import postCSSUrl from 'postcss-url'; import postCSSModules from 'postcss-modules'; -import postCSSImport from 'postcss-import'; import postCSSSimpleVars from 'postcss-simple-vars'; import cssNano from 'cssnano'; import camelCase from 'lodash.camelcase'; @@ -28,9 +27,19 @@ import glob from 'glob'; const globP = promisify(glob); const moduleSuffix = '.css'; -const prefix = 'css-bundle:'; +const sourcePrefix = 'css:'; +const addPrefix = 'add-css:'; const assetRe = new RegExp('/fake/path/to/asset/([^/]+)/', 'g'); +const appendCssModule = '\0appendCss'; +const appendCssSource = ` + export default function appendCss(css) { + const style = document.createElement('style'); + style.textContent = css; + document.head.append(style); + } +`; + export default function (resolveFileUrl) { /** @type {string[]} */ let emittedCSSIds; @@ -39,61 +48,6 @@ export default function (resolveFileUrl) { /** @type {Map} */ let pathToResult; - async function loadBundledCSS(path, rollupContext) { - const parsedPath = parsePath(path); - - if (!pathToResult.has(path)) { - throw Error(`Cannot find ${path} in pathToResult`); - } - - const file = pathToResult.get(path).css; - - const cssResult = await postcss([ - postCSSImport({ - path: ['./', 'static-build/'], - load(path) { - if (!pathToResult.has(path)) { - throw Error(`Cannot find ${path} in pathToResult`); - } - return pathToResult.get(path).css; - }, - }), - postCSSUrl({ - url: ({ relativePath, url }) => { - if (/^https?:\/\//.test(url)) return url; - const parsedPath = parsePath(relativePath); - const source = readFileSync(resolvePath(dirname(path), relativePath)); - const fileId = rollupContext.emitFile({ - type: 'asset', - name: parsedPath.base, - source, - }); - const hash = createHash('md5'); - hash.update(source); - const md5 = hash.digest('hex'); - hashToId.set(md5, fileId); - return `/fake/path/to/asset/${md5}/`; - }, - }), - cssNano, - ]).process(file, { - from: path, - }); - - const fileId = rollupContext.emitFile({ - type: 'asset', - source: cssResult.css, - name: parsedPath.base, - }); - - emittedCSSIds.push(fileId); - - return [ - `export default import.meta.ROLLUP_FILE_URL_${fileId}`, - `export const inline = ${JSON.stringify(cssResult.css)}`, - ].join('\n'); - } - return { name: 'css', async buildStart() { @@ -120,8 +74,28 @@ export default function (resolveFileUrl) { moduleJSON = json; }, }), + postCSSUrl({ + url: ({ relativePath, url }) => { + if (/^https?:\/\//.test(url)) return url; + const parsedPath = parsePath(relativePath); + const source = readFileSync( + resolvePath(dirname(path), relativePath), + ); + const fileId = this.emitFile({ + type: 'asset', + name: parsedPath.base, + source, + }); + const hash = createHash('md5'); + hash.update(source); + const md5 = hash.digest('hex'); + hashToId.set(md5, fileId); + return `/fake/path/to/asset/${md5}/`; + }, + }), + cssNano, ]).process(file, { - from: undefined, + from: path, }); const cssClassExports = Object.entries(moduleJSON).map( @@ -152,14 +126,45 @@ export default function (resolveFileUrl) { ); }, async resolveId(id, importer) { - if (!id.startsWith(prefix)) return; + if (id === appendCssModule) return id; + + const prefix = id.startsWith(sourcePrefix) + ? sourcePrefix + : id.startsWith(addPrefix) + ? addPrefix + : undefined; + + if (!prefix) return; + const resolved = await this.resolve(id.slice(prefix.length), importer); if (!resolved) throw Error(`Couldn't resolve ${id} from ${importer}`); + return prefix + resolved.id; }, async load(id) { - if (id.startsWith(prefix)) { - return loadBundledCSS(id.slice(prefix.length), this); + if (id === appendCssModule) return appendCssSource; + if (id.startsWith(sourcePrefix)) { + const path = id.slice(sourcePrefix.length); + + if (!pathToResult.has(path)) { + throw Error(`Cannot find ${path} in pathToResult`); + } + + const cssStr = JSON.stringify(pathToResult.get(path).css).replace( + assetRe, + (match, hash) => + `" + import.meta.ROLLUP_FILE_URL_${hashToId.get(hash)} + "`, + ); + + return `export default ${cssStr};`; + } + if (id.startsWith(addPrefix)) { + const path = id.slice(addPrefix.length); + return ( + `import css from 'css:${path}';\n` + + `import appendCss from '${appendCssModule}';\n` + + `appendCss(css);\n` + ); } if (id.endsWith(moduleSuffix)) { if (!pathToResult.has(id)) { @@ -169,27 +174,5 @@ export default function (resolveFileUrl) { return pathToResult.get(id).module; } }, - async generateBundle(options, bundle) { - const cssAssets = emittedCSSIds.map((id) => this.getFileName(id)); - - for (const cssAsset of cssAssets) { - bundle[cssAsset].source = bundle[cssAsset].source.replace( - assetRe, - (_, p1) => - resolveFileUrl({ - fileName: this.getFileName(hashToId.get(p1)), - }), - ); - } - - for (const item of Object.values(bundle)) { - if (item.type === 'asset') continue; - item.code = item.code.replace(assetRe, (match, p1) => - resolveFileUrl({ - fileName: this.getFileName(hashToId.get(p1)), - }), - ); - } - }, }; } diff --git a/missing-types.d.ts b/missing-types.d.ts index 32d82327..a1ed1d40 100644 --- a/missing-types.d.ts +++ b/missing-types.d.ts @@ -22,4 +22,9 @@ declare module 'omt:*' { export default value; } +declare module 'css:*' { + const source: string; + export default source; +} + declare const __PRODUCTION__: boolean; diff --git a/package-lock.json b/package-lock.json index ad624434..5c991af4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2795,12 +2795,6 @@ "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", "dev": true }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, "pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -3431,98 +3425,6 @@ } } }, - "postcss-import": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-12.0.1.tgz", - "integrity": "sha512-3Gti33dmCjyKBgimqGxL3vcV8w9+bsHwO5UrBawp796+jdardbcFl4RP5w/76BwNL7aGzpKstIfF9I+kdE8pTw==", - "dev": true, - "requires": { - "postcss": "^7.0.1", - "postcss-value-parser": "^3.2.3", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "7.0.34", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.34.tgz", - "integrity": "sha512-H/7V2VeNScX9KE83GDrDZNiGT1m2H+UTnlinIzhjlLX9hfMUn1mHNnGeX81a1c8JSBdBvqk7c2ZOG6ZPn5itGw==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, "postcss-merge-longhand": { "version": "4.0.11", "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", @@ -5975,15 +5877,6 @@ "strip-json-comments": "~2.0.1" } }, - "read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", - "dev": true, - "requires": { - "pify": "^2.3.0" - } - }, "registry-auth-token": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", diff --git a/package.json b/package.json index 012b0540..93a59a57 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,6 @@ "lint-staged": "^10.4.0", "lodash.camelcase": "^4.3.0", "postcss": "^8.0.8", - "postcss-import": "^12.0.1", "postcss-modules": "^3.2.2", "postcss-nested": "^5.0.0", "postcss-simple-vars": "^6.0.0", diff --git a/src/client/initial-app/App/index.tsx b/src/client/initial-app/App/index.tsx index 6afdc147..58d34f3f 100644 --- a/src/client/initial-app/App/index.tsx +++ b/src/client/initial-app/App/index.tsx @@ -6,6 +6,7 @@ import { h, Component } from 'preact'; import { linkRef } from 'client/initial-app/util'; import * as style from './style.css'; +import 'add-css:./style.css'; import 'file-drop-element'; import 'client/initial-app/custom-els/snack-bar'; //import Intro from '../intro'; diff --git a/src/client/missing-types.d.ts b/src/client/missing-types.d.ts index 530bd6c0..4cb316ca 100644 --- a/src/client/missing-types.d.ts +++ b/src/client/missing-types.d.ts @@ -21,4 +21,6 @@ interface Navigator { readonly standalone: boolean; } +declare module 'add-css:*' {} + declare module 'preact/debug' {} diff --git a/src/static-build/missing-types.d.ts b/src/static-build/missing-types.d.ts index 46dab4c5..799d7f26 100644 --- a/src/static-build/missing-types.d.ts +++ b/src/static-build/missing-types.d.ts @@ -17,9 +17,3 @@ declare module 'client-bundle:*' { export default url; export const imports: string[]; } - -declare module 'css-bundle:*' { - const url: string; - export default url; - export const inline: string; -} diff --git a/src/static-build/pages/index/index.tsx b/src/static-build/pages/index/index.tsx index 7c5f5423..9dec6889 100644 --- a/src/static-build/pages/index/index.tsx +++ b/src/static-build/pages/index/index.tsx @@ -12,9 +12,10 @@ */ import { h, FunctionalComponent } from 'preact'; -import styles from 'css-bundle:./all.css'; +import css from 'css:./all.css'; import clientBundleURL, { imports } from 'client-bundle:client/initial-app'; import favicon from 'url:static-build/assets/favicon.ico'; +import { escapeStyleScriptContent } from 'static-build/utils'; interface Props {} @@ -35,7 +36,9 @@ const Index: FunctionalComponent = () => ( - +