parent
c6b5909b93
commit
c491d1f423
4 changed files with 54 additions and 15 deletions
5
.changeset/honest-knives-bake.md
Normal file
5
.changeset/honest-knives-bake.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'astro': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Bugfix: Sass compile errors cause compiler panic
|
|
@ -6,7 +6,6 @@ import type { AstroConfig } from '../@types/astro';
|
||||||
import esbuild from 'esbuild';
|
import esbuild from 'esbuild';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
import os from 'os';
|
|
||||||
import { transform } from '@astrojs/compiler';
|
import { transform } from '@astrojs/compiler';
|
||||||
import { AstroDevServer } from '../core/dev/index.js';
|
import { AstroDevServer } from '../core/dev/index.js';
|
||||||
import { getViteTransform, TransformHook, transformWithVite } from './styles.js';
|
import { getViteTransform, TransformHook, transformWithVite } from './styles.js';
|
||||||
|
@ -32,13 +31,11 @@ function isSSR(options: undefined | boolean | { ssr: boolean }): boolean {
|
||||||
|
|
||||||
/** Transform .astro files for Vite */
|
/** Transform .astro files for Vite */
|
||||||
export default function astro({ config, devServer }: AstroPluginOptions): vite.Plugin {
|
export default function astro({ config, devServer }: AstroPluginOptions): vite.Plugin {
|
||||||
let platform: NodeJS.Platform;
|
|
||||||
let viteTransform: TransformHook;
|
let viteTransform: TransformHook;
|
||||||
return {
|
return {
|
||||||
name: '@astrojs/vite-plugin-astro',
|
name: '@astrojs/vite-plugin-astro',
|
||||||
enforce: 'pre', // run transforms before other plugins can
|
enforce: 'pre', // run transforms before other plugins can
|
||||||
configResolved(resolvedConfig) {
|
configResolved(resolvedConfig) {
|
||||||
platform = os.platform(); // TODO: remove macOS hack
|
|
||||||
viteTransform = getViteTransform(resolvedConfig);
|
viteTransform = getViteTransform(resolvedConfig);
|
||||||
},
|
},
|
||||||
// note: don’t claim .astro files with resolveId() — it prevents Vite from transpiling the final JS (import.meta.globEager, etc.)
|
// note: don’t claim .astro files with resolveId() — it prevents Vite from transpiling the final JS (import.meta.globEager, etc.)
|
||||||
|
@ -52,6 +49,7 @@ export default function astro({ config, devServer }: AstroPluginOptions): vite.P
|
||||||
const isPage = normalizedID.startsWith(fileURLToPath(config.pages)) || normalizedID.startsWith(fileURLToPath(config.layouts));
|
const isPage = normalizedID.startsWith(fileURLToPath(config.pages)) || normalizedID.startsWith(fileURLToPath(config.layouts));
|
||||||
let source = await fs.promises.readFile(id, 'utf8');
|
let source = await fs.promises.readFile(id, 'utf8');
|
||||||
let tsResult: TransformResult | undefined;
|
let tsResult: TransformResult | undefined;
|
||||||
|
let cssTransformError: Error | undefined;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Transform from `.astro` to valid `.ts`
|
// Transform from `.astro` to valid `.ts`
|
||||||
|
@ -65,23 +63,29 @@ export default function astro({ config, devServer }: AstroPluginOptions): vite.P
|
||||||
internalURL: 'astro/internal',
|
internalURL: 'astro/internal',
|
||||||
preprocessStyle: async (value: string, attrs: Record<string, string>) => {
|
preprocessStyle: async (value: string, attrs: Record<string, string>) => {
|
||||||
const lang = `.${attrs?.lang || 'css'}`.toLowerCase();
|
const lang = `.${attrs?.lang || 'css'}`.toLowerCase();
|
||||||
const result = await transformWithVite({ value, lang, id, transformHook: viteTransform, ssr: isSSR(opts) });
|
try {
|
||||||
if (!result) {
|
const result = await transformWithVite({ value, lang, id, transformHook: viteTransform, ssr: isSSR(opts) });
|
||||||
// TODO: compiler supports `null`, but types don't yet
|
let map: SourceMapInput | undefined;
|
||||||
return result as any;
|
if (!result) return null as any; // TODO: add type in compiler to fix "any"
|
||||||
}
|
if (result.map) {
|
||||||
let map: SourceMapInput | undefined;
|
if (typeof result.map === 'string') {
|
||||||
if (result.map) {
|
map = result.map;
|
||||||
if (typeof result.map === 'string') {
|
} else if (result.map.mappings) {
|
||||||
map = result.map;
|
map = result.map.toString();
|
||||||
} 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;
|
||||||
}
|
}
|
||||||
return { code: result.code, map };
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// throw CSS transform errors here if encountered
|
||||||
|
if (cssTransformError) throw cssTransformError;
|
||||||
|
|
||||||
// Compile `.ts` to `.js`
|
// Compile `.ts` to `.js`
|
||||||
const { code, map } = await esbuild.transform(tsResult.code, { loader: 'ts', sourcemap: 'external', sourcefile: id });
|
const { code, map } = await esbuild.transform(tsResult.code, { loader: 'ts', sourcemap: 'external', sourcefile: id });
|
||||||
|
|
||||||
|
|
7
packages/astro/test/fixtures/sass/src/pages/error.astro
vendored
Normal file
7
packages/astro/test/fixtures/sass/src/pages/error.astro
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<style lang="scss">
|
||||||
|
$blue: blue;
|
||||||
|
|
||||||
|
.h1 {
|
||||||
|
color: $red; // undefined!
|
||||||
|
}
|
||||||
|
</style>
|
23
packages/astro/test/sass.test.js
Normal file
23
packages/astro/test/sass.test.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { expect } from 'chai';
|
||||||
|
import { loadFixture } from './test-utils.js';
|
||||||
|
|
||||||
|
// note: many Sass tests live in 0-css.test.js to test within context of a framework.
|
||||||
|
// these tests are independent of framework.
|
||||||
|
describe('Sass', () => {
|
||||||
|
let fixture;
|
||||||
|
let devServer;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
fixture = await loadFixture({ projectRoot: './fixtures/sass/' });
|
||||||
|
devServer = await fixture.startDevServer();
|
||||||
|
});
|
||||||
|
|
||||||
|
after(async () => {
|
||||||
|
devServer && (await devServer.stop());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows helpful error on failure', async () => {
|
||||||
|
const res = await fixture.fetch('/error').then((res) => res.text());
|
||||||
|
expect(res).to.include('Undefined variable');
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in a new issue