From 6040092e987431c288b497bc4ebb7925a2a5d7f2 Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Thu, 8 Sep 2022 10:37:22 -0400 Subject: [PATCH 1/7] Prevent locking up when encountering invalid CSS --- packages/astro/package.json | 5 +- .../compile}/compile.ts | 89 ++++--------------- packages/astro/src/core/compile/index.ts | 13 +++ packages/astro/src/core/compile/style.ts | 38 ++++++++ .../src/core/compile/test/invalid-css.test.js | 37 ++++++++ packages/astro/src/core/compile/types.ts | 9 ++ packages/astro/src/vite-plugin-astro/hmr.ts | 2 +- packages/astro/src/vite-plugin-astro/index.ts | 29 +++--- .../src/vite-plugin-markdown-legacy/index.ts | 19 ++-- .../astro/src/vite-style-transform/index.ts | 7 ++ .../vite-style-transform/style-transform.ts | 49 ++++++++++ .../transform-with-vite.ts} | 0 pnpm-lock.yaml | 8 +- 13 files changed, 201 insertions(+), 104 deletions(-) rename packages/astro/src/{vite-plugin-astro => core/compile}/compile.ts (54%) create mode 100644 packages/astro/src/core/compile/index.ts create mode 100644 packages/astro/src/core/compile/style.ts create mode 100644 packages/astro/src/core/compile/test/invalid-css.test.js create mode 100644 packages/astro/src/core/compile/types.ts create mode 100644 packages/astro/src/vite-style-transform/index.ts create mode 100644 packages/astro/src/vite-style-transform/style-transform.ts rename packages/astro/src/{vite-plugin-astro/styles.ts => vite-style-transform/transform-with-vite.ts} (100%) diff --git a/packages/astro/package.json b/packages/astro/package.json index 82c848ad37bb..94386af5ece1 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -88,13 +88,14 @@ "dev": "astro-scripts dev --prebuild \"src/runtime/server/astro-island.ts\" --prebuild \"src/runtime/client/{idle,load,media,only,visible}.ts\" \"src/**/*.ts\"", "postbuild": "astro-scripts copy \"src/**/*.astro\"", "benchmark": "node test/benchmark/dev.bench.js && node test/benchmark/build.bench.js", - "test": "mocha --exit --timeout 20000 --ignore **/lit-element.test.js && mocha --timeout 20000 **/lit-element.test.js", + "test:unit": "mocha --exit --timeout 2000 src/**/test/*.test.js", + "test": "pnpm run test:unit && mocha --exit --timeout 20000 --ignore **/lit-element.test.js && mocha --timeout 20000 **/lit-element.test.js", "test:match": "mocha --timeout 20000 -g", "test:e2e": "playwright test", "test:e2e:match": "playwright test -g" }, "dependencies": { - "@astrojs/compiler": "^0.23.5", + "@astrojs/compiler": "^0.24.0", "@astrojs/language-server": "^0.23.0", "@astrojs/markdown-remark": "^1.1.1", "@astrojs/telemetry": "^1.0.0", diff --git a/packages/astro/src/vite-plugin-astro/compile.ts b/packages/astro/src/core/compile/compile.ts similarity index 54% rename from packages/astro/src/vite-plugin-astro/compile.ts rename to packages/astro/src/core/compile/compile.ts index 368e310fc3c1..387cfd823d4c 100644 --- a/packages/astro/src/vite-plugin-astro/compile.ts +++ b/packages/astro/src/core/compile/compile.ts @@ -1,14 +1,12 @@ import type { TransformResult } from '@astrojs/compiler'; -import type { PluginContext, SourceMapInput } from 'rollup'; -import type { ViteDevServer } from 'vite'; -import type { AstroConfig } from '../@types/astro'; -import type { TransformStyleWithVite } from './styles'; +import type { AstroConfig } from '../../@types/astro'; +import type { TransformStyle } from './types'; import { transform } from '@astrojs/compiler'; -import { fileURLToPath } from 'url'; -import { AstroErrorCodes } from '../core/errors.js'; -import { prependForwardSlash } from '../core/path.js'; -import { viteID } from '../core/util.js'; +import { AstroErrorCodes } from '../errors.js'; +import { prependForwardSlash } from '../path.js'; +import { viteID } from '../util.js'; +import { createStylePreprocessor } from './style.js'; type CompilationCache = Map; type CompileResult = TransformResult & { @@ -23,20 +21,7 @@ export interface CompileProps { filename: string; moduleId: string; source: string; - ssr: boolean; - transformStyleWithVite: TransformStyleWithVite; - viteDevServer?: ViteDevServer; - pluginContext: PluginContext; -} - -function getNormalizedID(filename: string): string { - try { - const filenameURL = new URL(`file://${filename}`); - return fileURLToPath(filenameURL); - } catch (err) { - // Not a real file, so just use the provided filename as the normalized id - return filename; - } + transformStyle: TransformStyle; } async function compile({ @@ -44,20 +29,11 @@ async function compile({ filename, moduleId, source, - ssr, - transformStyleWithVite, - viteDevServer, - pluginContext, + transformStyle, }: CompileProps): Promise { - const normalizedID = getNormalizedID(filename); let cssDeps = new Set(); let cssTransformError: Error | undefined; - // handleHotUpdate doesn't have `addWatchFile` used by transformStyleWithVite. - if (!pluginContext.addWatchFile) { - pluginContext.addWatchFile = () => {}; - } - // Transform from `.astro` to valid `.ts` // use `sourcemap: "both"` so that sourcemap is included in the code // result passed to esbuild, but also available in the catch handler. @@ -69,44 +45,11 @@ async function compile({ sourcefile: filename, sourcemap: 'both', internalURL: `/@fs${prependForwardSlash( - viteID(new URL('../runtime/server/index.js', import.meta.url)) + viteID(new URL('../../runtime/server/index.js', import.meta.url)) )}`, // TODO: baseline flag experimentalStaticExtraction: true, - preprocessStyle: async (value: string, attrs: Record) => { - const lang = `.${attrs?.lang || 'css'}`.toLowerCase(); - - try { - const result = await transformStyleWithVite.call(pluginContext, { - id: normalizedID, - source: value, - lang, - ssr, - viteDevServer, - }); - - if (!result) return null as any; // TODO: add type in compiler to fix "any" - - for (const dep of result.deps) { - cssDeps.add(dep); - } - - let map: SourceMapInput | undefined; - if (result.map) { - if (typeof result.map === 'string') { - map = result.map; - } else if (result.map.mappings) { - map = result.map.toString(); - } - } - - return { code: result.code, map }; - } catch (err) { - // save error to throw in plugin context - cssTransformError = err as any; - return null; - } - }, + preprocessStyle: createStylePreprocessor(transformStyle, cssDeps), }) .catch((err) => { // throw compiler errors here if encountered @@ -114,12 +57,14 @@ async function compile({ throw err; }) .then((result) => { - // throw CSS transform errors here if encountered - if (cssTransformError) { - (cssTransformError as any).code = - (cssTransformError as any).code || AstroErrorCodes.UnknownCompilerCSSError; - throw cssTransformError; + if(result.styleError.length) { + const aggregateError = new AggregateError(result.styleError.map(message => { + return new Error(message); + })); + (aggregateError as any).code = AstroErrorCodes.UnknownCompilerCSSError; + throw aggregateError; } + return result; }); diff --git a/packages/astro/src/core/compile/index.ts b/packages/astro/src/core/compile/index.ts new file mode 100644 index 000000000000..7d3539c2ebec --- /dev/null +++ b/packages/astro/src/core/compile/index.ts @@ -0,0 +1,13 @@ +export type { + CompileProps +} from './compile'; +export type { + TransformStyle +} from './types'; + +export { + cachedCompilation, + invalidateCompilation, + isCached, + getCachedSource +} from './compile.js'; diff --git a/packages/astro/src/core/compile/style.ts b/packages/astro/src/core/compile/style.ts new file mode 100644 index 000000000000..eaf79a565c1c --- /dev/null +++ b/packages/astro/src/core/compile/style.ts @@ -0,0 +1,38 @@ +import type { TransformOptions } from '@astrojs/compiler'; +import type { TransformStyle } from './types'; +import type { SourceMapInput } from 'rollup'; + +type PreprocessStyle = TransformOptions['preprocessStyle']; + +export function createStylePreprocessor(transformStyle: TransformStyle, cssDeps: Set): PreprocessStyle { + const preprocessStyle: PreprocessStyle = async (value: string, attrs: Record) => { + const lang = `.${attrs?.lang || 'css'}`.toLowerCase(); + + try { + const result = await transformStyle(value, lang); + + if (!result) return null as any; // TODO: add type in compiler to fix "any" + + for (const dep of result.deps) { + cssDeps.add(dep); + } + + let map: SourceMapInput | undefined; + if (result.map) { + if (typeof result.map === 'string') { + map = result.map; + } else if (result.map.mappings) { + map = result.map.toString(); + } + } + + return { code: result.code, map }; + } catch (err) { + return { + error: err + '', + }; + } + }; + + return preprocessStyle; +}; diff --git a/packages/astro/src/core/compile/test/invalid-css.test.js b/packages/astro/src/core/compile/test/invalid-css.test.js new file mode 100644 index 000000000000..b7336375ef0b --- /dev/null +++ b/packages/astro/src/core/compile/test/invalid-css.test.js @@ -0,0 +1,37 @@ +/// +import { expect } from 'chai'; +import { cachedCompilation } from '../../../../dist/core/compile/index.js'; + +describe('astro/src/core/compile', () => { + describe('Invalid CSS', () => { + it('throws an aggregate error with the errors', async () => { + let error; + try { + let r = await cachedCompilation({ + config: /** @type {any} */({ + root: '/' + }), + filename: '/src/pages/index.astro', + moduleId: '/src/pages/index.astro', + source: ` + --- + --- + + `, + transformStyle(source, lang) { + throw new Error('Invalid css'); + } + }); + } catch(err) { + error = err; + } + + expect(error).to.be.an.instanceOf(AggregateError); + expect(error.errors[0].message).to.contain('Invalid css'); + }); + }); +}); diff --git a/packages/astro/src/core/compile/types.ts b/packages/astro/src/core/compile/types.ts new file mode 100644 index 000000000000..0e7f79fb2c98 --- /dev/null +++ b/packages/astro/src/core/compile/types.ts @@ -0,0 +1,9 @@ +import type { SourceMap } from 'rollup'; + +export type TransformStyleResult = null | { + code: string; + map: SourceMap | null; + deps: Set; +} + +export type TransformStyle = (source: string, lang: string) => TransformStyleResult | Promise; diff --git a/packages/astro/src/vite-plugin-astro/hmr.ts b/packages/astro/src/vite-plugin-astro/hmr.ts index 6ee123e07763..9c729715dd7a 100644 --- a/packages/astro/src/vite-plugin-astro/hmr.ts +++ b/packages/astro/src/vite-plugin-astro/hmr.ts @@ -4,7 +4,7 @@ import type { AstroConfig } from '../@types/astro'; import type { LogOptions } from '../core/logger/core.js'; import { info } from '../core/logger/core.js'; import * as msg from '../core/messages.js'; -import { cachedCompilation, invalidateCompilation, isCached } from './compile.js'; +import { cachedCompilation, invalidateCompilation, isCached } from '../core/compile/index.js'; import { isAstroScript } from './query.js'; const PKG_PREFIX = new URL('../../', import.meta.url); diff --git a/packages/astro/src/vite-plugin-astro/index.ts b/packages/astro/src/vite-plugin-astro/index.ts index ccfba7af7036..f8b98a6a4d12 100644 --- a/packages/astro/src/vite-plugin-astro/index.ts +++ b/packages/astro/src/vite-plugin-astro/index.ts @@ -3,6 +3,7 @@ import type * as vite from 'vite'; import type { AstroConfig } from '../@types/astro'; import type { LogOptions } from '../core/logger/core.js'; import type { PluginMetadata as AstroPluginMetadata } from './types'; +import type { ViteStyleTransformer } from '../vite-style-transform'; import ancestor from 'common-ancestor-path'; import esbuild from 'esbuild'; @@ -10,10 +11,14 @@ import slash from 'slash'; import { fileURLToPath } from 'url'; import { isRelativePath, startsWithForwardSlash } from '../core/path.js'; import { getFileInfo } from '../vite-plugin-utils/index.js'; -import { cachedCompilation, CompileProps, getCachedSource } from './compile.js'; +import { + cachedCompilation, + CompileProps, + getCachedSource +} from '../core/compile/index.js'; import { handleHotUpdate } from './hmr.js'; import { parseAstroRequest, ParsedRequestResult } from './query.js'; -import { createTransformStyleWithViteFn, TransformStyleWithVite } from './styles.js'; +import { createViteStyleTransformer, createTransformStyles } from '../vite-style-transform/index.js'; const FRONTMATTER_PARSE_REGEXP = /^\-\-\-(.*)^\-\-\-/ms; interface AstroPluginOptions { @@ -38,7 +43,7 @@ export default function astro({ config, logging }: AstroPluginOptions): vite.Plu } let resolvedConfig: vite.ResolvedConfig; - let transformStyleWithVite: TransformStyleWithVite; + let styleTransformer: ViteStyleTransformer; let viteDevServer: vite.ViteDevServer | undefined; // Variables for determining if an id starts with /src... @@ -60,10 +65,11 @@ export default function astro({ config, logging }: AstroPluginOptions): vite.Plu enforce: 'pre', // run transforms before other plugins can configResolved(_resolvedConfig) { resolvedConfig = _resolvedConfig; - transformStyleWithVite = createTransformStyleWithViteFn(_resolvedConfig); + styleTransformer = createViteStyleTransformer(_resolvedConfig); }, configureServer(server) { viteDevServer = server; + styleTransformer.viteDevServer = server; }, // note: don’t claim .astro files with resolveId() — it prevents Vite from transpiling the final JS (import.meta.glob, etc.) async resolveId(id, from, opts) { @@ -117,10 +123,7 @@ export default function astro({ config, logging }: AstroPluginOptions): vite.Plu filename, moduleId: id, source, - ssr: Boolean(opts?.ssr), - transformStyleWithVite, - viteDevServer, - pluginContext: this, + transformStyle: createTransformStyles(styleTransformer, filename, Boolean(opts?.ssr), this), }; switch (query.type) { @@ -216,10 +219,7 @@ export default function astro({ config, logging }: AstroPluginOptions): vite.Plu filename, moduleId: id, source, - ssr: Boolean(opts?.ssr), - transformStyleWithVite, - viteDevServer, - pluginContext: this, + transformStyle: createTransformStyles(styleTransformer, filename, Boolean(opts?.ssr), this), }; try { @@ -352,10 +352,7 @@ ${source} filename: context.file, moduleId: context.file, source: await context.read(), - ssr: true, - transformStyleWithVite, - viteDevServer, - pluginContext: this, + transformStyle: createTransformStyles(styleTransformer, context.file, true, this), }; const compile = () => cachedCompilation(compileProps); return handleHotUpdate.call(this, context, { diff --git a/packages/astro/src/vite-plugin-markdown-legacy/index.ts b/packages/astro/src/vite-plugin-markdown-legacy/index.ts index 1bfb8546b02d..cd26a7ba613e 100644 --- a/packages/astro/src/vite-plugin-markdown-legacy/index.ts +++ b/packages/astro/src/vite-plugin-markdown-legacy/index.ts @@ -9,11 +9,8 @@ import type { AstroConfig } from '../@types/astro'; import { pagesVirtualModuleId } from '../core/app/index.js'; import { collectErrorMetadata } from '../core/errors.js'; import type { LogOptions } from '../core/logger/core.js'; -import { cachedCompilation, CompileProps } from '../vite-plugin-astro/compile.js'; -import { - createTransformStyleWithViteFn, - TransformStyleWithVite, -} from '../vite-plugin-astro/styles.js'; +import { cachedCompilation, CompileProps } from '../core/compile/index.js'; +import { ViteStyleTransformer, createViteStyleTransformer, createTransformStyles } from '../vite-style-transform/index.js'; import type { PluginMetadata as AstroPluginMetadata } from '../vite-plugin-astro/types'; import { getFileInfo } from '../vite-plugin-utils/index.js'; @@ -64,14 +61,17 @@ export default function markdown({ config, logging }: AstroPluginOptions): Plugi return false; } - let transformStyleWithVite: TransformStyleWithVite; + let styleTransformer: ViteStyleTransformer; let viteDevServer: ViteDevServer | undefined; return { name: 'astro:markdown', enforce: 'pre', configResolved(_resolvedConfig) { - transformStyleWithVite = createTransformStyleWithViteFn(_resolvedConfig); + styleTransformer = createViteStyleTransformer(_resolvedConfig); + }, + configureServer(server) { + styleTransformer.viteDevServer = server; }, async resolveId(id, importer, options) { // Resolve any .md files with the `?content` cache buster. This should only come from @@ -208,10 +208,7 @@ ${setup}`.trim(); filename, moduleId: id, source: astroResult, - ssr: Boolean(opts?.ssr), - transformStyleWithVite, - viteDevServer, - pluginContext: this, + transformStyle: createTransformStyles(styleTransformer, filename, Boolean(opts?.ssr), this), }; let transformResult = await cachedCompilation(compileProps); diff --git a/packages/astro/src/vite-style-transform/index.ts b/packages/astro/src/vite-style-transform/index.ts new file mode 100644 index 000000000000..f3635cba7184 --- /dev/null +++ b/packages/astro/src/vite-style-transform/index.ts @@ -0,0 +1,7 @@ +export type { + ViteStyleTransformer +} from './style-transform'; +export { + createViteStyleTransformer, + createTransformStyles +} from './style-transform.js'; diff --git a/packages/astro/src/vite-style-transform/style-transform.ts b/packages/astro/src/vite-style-transform/style-transform.ts new file mode 100644 index 000000000000..aebec24408d3 --- /dev/null +++ b/packages/astro/src/vite-style-transform/style-transform.ts @@ -0,0 +1,49 @@ +import type { PluginContext } from 'rollup'; +import type { TransformStyle } from '../core/compile/index'; +import { TransformStyleWithVite, createTransformStyleWithViteFn } from './transform-with-vite.js'; +import { fileURLToPath } from 'url'; + +import type * as vite from 'vite'; + +export type ViteStyleTransformer = { + viteDevServer?: vite.ViteDevServer; + transformStyleWithVite: TransformStyleWithVite; +} + +export function createViteStyleTransformer(viteConfig: vite.ResolvedConfig): ViteStyleTransformer { + return { + transformStyleWithVite: createTransformStyleWithViteFn(viteConfig), + }; +} + +function getNormalizedIDForPostCSS(filename: string): string { + try { + const filenameURL = new URL(`file://${filename}`); + return fileURLToPath(filenameURL); + } catch (err) { + // Not a real file, so just use the provided filename as the normalized id + return filename; + } +} + +export function createTransformStyles(viteStyleTransformer: ViteStyleTransformer, filename: string, ssr: boolean, pluginContext: PluginContext): TransformStyle { + // handleHotUpdate doesn't have `addWatchFile` used by transformStyleWithVite. + // TODO, refactor, why is this happening *here* ? + if (!pluginContext.addWatchFile) { + pluginContext.addWatchFile = () => {}; + } + + const normalizedID = getNormalizedIDForPostCSS(filename); + + return async function(styleSource, lang) { + const result = await viteStyleTransformer.transformStyleWithVite.call(pluginContext, { + id: normalizedID, + source: styleSource, + lang, + ssr, + viteDevServer: viteStyleTransformer.viteDevServer, + }); + + return result; + }; +} diff --git a/packages/astro/src/vite-plugin-astro/styles.ts b/packages/astro/src/vite-style-transform/transform-with-vite.ts similarity index 100% rename from packages/astro/src/vite-plugin-astro/styles.ts rename to packages/astro/src/vite-style-transform/transform-with-vite.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 349a731dc8bd..79ae3fd4be9d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -338,7 +338,7 @@ importers: packages/astro: specifiers: - '@astrojs/compiler': ^0.23.5 + '@astrojs/compiler': ^0.24.0 '@astrojs/language-server': ^0.23.0 '@astrojs/markdown-remark': ^1.1.1 '@astrojs/telemetry': ^1.0.0 @@ -421,7 +421,7 @@ importers: yargs-parser: ^21.0.1 zod: ^3.17.3 dependencies: - '@astrojs/compiler': 0.23.5 + '@astrojs/compiler': 0.24.0 '@astrojs/language-server': 0.23.3 '@astrojs/markdown-remark': link:../markdown/remark '@astrojs/telemetry': link:../telemetry @@ -3232,6 +3232,10 @@ packages: resolution: {integrity: sha512-vBMPy9ok4iLapSyCCT1qsZ9dK7LkVFl9mObtLEmWiec9myGHS9h2kQY2xzPeFNJiWXUf9O6tSyQpQTy5As/p3g==} dev: false + /@astrojs/compiler/0.24.0: + resolution: {integrity: sha512-xZ81C/oMfExdF18I1Tyd2BKKzBqO+qYYctSy4iCwH4UWSo/4Y8A8MAzV1hG67uuE7hFRourSl6H5KUbhyChv/A==} + dev: false + /@astrojs/language-server/0.23.3: resolution: {integrity: sha512-ROoMKo37NZ76pE/A2xHfjDlgfsNnFmkhL4+Wifs0L855n73SUCbnXz7ZaQktIGAq2Te2TpSjAawiOx0q9L5qeg==} hasBin: true From 6a3ff7913a86b59397bfa3bfc8caacc524fb6e2e Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Thu, 8 Sep 2022 12:32:22 -0400 Subject: [PATCH 2/7] Add a changeset --- .changeset/eleven-goats-confess.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/eleven-goats-confess.md diff --git a/.changeset/eleven-goats-confess.md b/.changeset/eleven-goats-confess.md new file mode 100644 index 000000000000..a65c2c78340a --- /dev/null +++ b/.changeset/eleven-goats-confess.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Prevent locking up when encountering invalid CSS From 866dbf13cb043fc24c5ac55641703b588594391e Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Thu, 8 Sep 2022 13:38:39 -0400 Subject: [PATCH 3/7] Move the unit test to the test folder --- packages/astro/package.json | 2 +- .../compile/test => test/units/compile}/invalid-css.test.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename packages/astro/{src/core/compile/test => test/units/compile}/invalid-css.test.js (87%) diff --git a/packages/astro/package.json b/packages/astro/package.json index 94386af5ece1..1b563b50f409 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -88,7 +88,7 @@ "dev": "astro-scripts dev --prebuild \"src/runtime/server/astro-island.ts\" --prebuild \"src/runtime/client/{idle,load,media,only,visible}.ts\" \"src/**/*.ts\"", "postbuild": "astro-scripts copy \"src/**/*.astro\"", "benchmark": "node test/benchmark/dev.bench.js && node test/benchmark/build.bench.js", - "test:unit": "mocha --exit --timeout 2000 src/**/test/*.test.js", + "test:unit": "mocha --exit --timeout 2000 ./test/units/**/*.test.js", "test": "pnpm run test:unit && mocha --exit --timeout 20000 --ignore **/lit-element.test.js && mocha --timeout 20000 **/lit-element.test.js", "test:match": "mocha --timeout 20000 -g", "test:e2e": "playwright test", diff --git a/packages/astro/src/core/compile/test/invalid-css.test.js b/packages/astro/test/units/compile/invalid-css.test.js similarity index 87% rename from packages/astro/src/core/compile/test/invalid-css.test.js rename to packages/astro/test/units/compile/invalid-css.test.js index b7336375ef0b..51543f172fc8 100644 --- a/packages/astro/src/core/compile/test/invalid-css.test.js +++ b/packages/astro/test/units/compile/invalid-css.test.js @@ -1,6 +1,6 @@ -/// + import { expect } from 'chai'; -import { cachedCompilation } from '../../../../dist/core/compile/index.js'; +import { cachedCompilation } from '../../../dist/core/compile/index.js'; describe('astro/src/core/compile', () => { describe('Invalid CSS', () => { From 0f75219e444366f0d277eeae74beee71a3aa0ff9 Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Thu, 8 Sep 2022 13:55:50 -0400 Subject: [PATCH 4/7] ponyfill aggregateerror --- packages/astro/src/core/compile/compile.ts | 2 +- packages/astro/src/core/util.ts | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/astro/src/core/compile/compile.ts b/packages/astro/src/core/compile/compile.ts index 387cfd823d4c..fc728bd74dae 100644 --- a/packages/astro/src/core/compile/compile.ts +++ b/packages/astro/src/core/compile/compile.ts @@ -5,7 +5,7 @@ import type { TransformStyle } from './types'; import { transform } from '@astrojs/compiler'; import { AstroErrorCodes } from '../errors.js'; import { prependForwardSlash } from '../path.js'; -import { viteID } from '../util.js'; +import { viteID, AggregateError } from '../util.js'; import { createStylePreprocessor } from './style.js'; type CompilationCache = Map; diff --git a/packages/astro/src/core/util.ts b/packages/astro/src/core/util.ts index 3c74b5580b31..3dc5a698e350 100644 --- a/packages/astro/src/core/util.ts +++ b/packages/astro/src/core/util.ts @@ -226,3 +226,11 @@ export async function resolveIdToUrl(viteServer: ViteDevServer, id: string) { } return VALID_ID_PREFIX + result.id; } + +export const AggregateError = typeof globalThis.AggregateError !== 'undefined' ? globalThis.AggregateError : class extends Error { + errors: Array = []; + constructor( errors: Iterable, message?: string | undefined) { + super(message); + this.errors = Array.from(errors); + } +} From 1e5fd859ff292d23390ce9b485c11ad94ab71afd Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Thu, 8 Sep 2022 14:02:08 -0400 Subject: [PATCH 5/7] Use the exported type --- packages/astro/test/units/compile/invalid-css.test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/astro/test/units/compile/invalid-css.test.js b/packages/astro/test/units/compile/invalid-css.test.js index 51543f172fc8..89081b54dd57 100644 --- a/packages/astro/test/units/compile/invalid-css.test.js +++ b/packages/astro/test/units/compile/invalid-css.test.js @@ -1,6 +1,7 @@ import { expect } from 'chai'; import { cachedCompilation } from '../../../dist/core/compile/index.js'; +import { AggregateError } from '../../../dist/core/util.js'; describe('astro/src/core/compile', () => { describe('Invalid CSS', () => { From 75e2778016ba9fe71df89cc5cc033a8b78139b30 Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Thu, 8 Sep 2022 15:08:58 -0400 Subject: [PATCH 6/7] keep original errors --- packages/astro/src/core/compile/compile.ts | 26 +++++++++++++--------- packages/astro/src/core/compile/style.ts | 5 +++-- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/packages/astro/src/core/compile/compile.ts b/packages/astro/src/core/compile/compile.ts index fc728bd74dae..d1c5c2f4b2d8 100644 --- a/packages/astro/src/core/compile/compile.ts +++ b/packages/astro/src/core/compile/compile.ts @@ -32,7 +32,7 @@ async function compile({ transformStyle, }: CompileProps): Promise { let cssDeps = new Set(); - let cssTransformError: Error | undefined; + let cssTransformErrors: Error[] = []; // Transform from `.astro` to valid `.ts` // use `sourcemap: "both"` so that sourcemap is included in the code @@ -49,7 +49,7 @@ async function compile({ )}`, // TODO: baseline flag experimentalStaticExtraction: true, - preprocessStyle: createStylePreprocessor(transformStyle, cssDeps), + preprocessStyle: createStylePreprocessor(transformStyle, cssDeps, cssTransformErrors), }) .catch((err) => { // throw compiler errors here if encountered @@ -57,15 +57,21 @@ async function compile({ throw err; }) .then((result) => { - if(result.styleError.length) { - const aggregateError = new AggregateError(result.styleError.map(message => { - return new Error(message); - })); - (aggregateError as any).code = AstroErrorCodes.UnknownCompilerCSSError; - throw aggregateError; + switch(cssTransformErrors.length) { + case 0: return result; + case 1: { + let error = cssTransformErrors[0]; + if(!(error as any).code) { + (error as any).code = AstroErrorCodes.UnknownCompilerCSSError; + } + throw cssTransformErrors[0]; + } + default: { + const aggregateError = new AggregateError(cssTransformErrors); + (aggregateError as any).code = AstroErrorCodes.UnknownCompilerCSSError; + throw aggregateError; + } } - - return result; }); const compileResult: CompileResult = Object.create(transformResult, { diff --git a/packages/astro/src/core/compile/style.ts b/packages/astro/src/core/compile/style.ts index eaf79a565c1c..fde1e60bd9db 100644 --- a/packages/astro/src/core/compile/style.ts +++ b/packages/astro/src/core/compile/style.ts @@ -4,7 +4,7 @@ import type { SourceMapInput } from 'rollup'; type PreprocessStyle = TransformOptions['preprocessStyle']; -export function createStylePreprocessor(transformStyle: TransformStyle, cssDeps: Set): PreprocessStyle { +export function createStylePreprocessor(transformStyle: TransformStyle, cssDeps: Set, errors: Error[]): PreprocessStyle { const preprocessStyle: PreprocessStyle = async (value: string, attrs: Record) => { const lang = `.${attrs?.lang || 'css'}`.toLowerCase(); @@ -28,8 +28,9 @@ export function createStylePreprocessor(transformStyle: TransformStyle, cssDeps: return { code: result.code, map }; } catch (err) { + errors.push(err as unknown as Error); return { - error: err + '', + error: err + '' }; } }; From f69108035d05e874e58ce91237cbe492ce77c0c8 Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Thu, 8 Sep 2022 15:13:05 -0400 Subject: [PATCH 7/7] fix --- packages/astro/test/units/compile/invalid-css.test.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/astro/test/units/compile/invalid-css.test.js b/packages/astro/test/units/compile/invalid-css.test.js index 89081b54dd57..72309c445c90 100644 --- a/packages/astro/test/units/compile/invalid-css.test.js +++ b/packages/astro/test/units/compile/invalid-css.test.js @@ -22,6 +22,11 @@ describe('astro/src/core/compile', () => { color: purple; } + `, transformStyle(source, lang) { throw new Error('Invalid css');