Skip to content

Commit

Permalink
Fix Sass WASM crashes (#2049)
Browse files Browse the repository at this point in the history
Partially addresses #2032
  • Loading branch information
drwpow committed Nov 29, 2021
1 parent c6b5909 commit c491d1f
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 15 deletions.
5 changes: 5 additions & 0 deletions .changeset/honest-knives-bake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Bugfix: Sass compile errors cause compiler panic
34 changes: 19 additions & 15 deletions packages/astro/src/vite-plugin-astro/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import type { AstroConfig } from '../@types/astro';
import esbuild from 'esbuild';
import fs from 'fs';
import { fileURLToPath } from 'url';
import os from 'os';
import { transform } from '@astrojs/compiler';
import { AstroDevServer } from '../core/dev/index.js';
import { getViteTransform, TransformHook, transformWithVite } from './styles.js';
Expand All @@ -32,13 +31,11 @@ function isSSR(options: undefined | boolean | { ssr: boolean }): boolean {

/** Transform .astro files for Vite */
export default function astro({ config, devServer }: AstroPluginOptions): vite.Plugin {
let platform: NodeJS.Platform;
let viteTransform: TransformHook;
return {
name: '@astrojs/vite-plugin-astro',
enforce: 'pre', // run transforms before other plugins can
configResolved(resolvedConfig) {
platform = os.platform(); // TODO: remove macOS hack
viteTransform = getViteTransform(resolvedConfig);
},
// note: don’t claim .astro files with resolveId() — it prevents Vite from transpiling the final JS (import.meta.globEager, etc.)
Expand All @@ -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));
let source = await fs.promises.readFile(id, 'utf8');
let tsResult: TransformResult | undefined;
let cssTransformError: Error | undefined;

try {
// Transform from `.astro` to valid `.ts`
Expand All @@ -65,23 +63,29 @@ export default function astro({ config, devServer }: AstroPluginOptions): vite.P
internalURL: 'astro/internal',
preprocessStyle: async (value: string, attrs: Record<string, string>) => {
const lang = `.${attrs?.lang || 'css'}`.toLowerCase();
const result = await transformWithVite({ value, lang, id, transformHook: viteTransform, ssr: isSSR(opts) });
if (!result) {
// TODO: compiler supports `null`, but types don't yet
return result as any;
}
let map: SourceMapInput | undefined;
if (result.map) {
if (typeof result.map === 'string') {
map = result.map;
} else if (result.map.mappings) {
map = result.map.toString();
try {
const result = await transformWithVite({ value, lang, id, transformHook: viteTransform, ssr: isSSR(opts) });
let map: SourceMapInput | undefined;
if (!result) return null as any; // TODO: add type in compiler to fix "any"
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;
}
return { code: result.code, map };
},
});

// throw CSS transform errors here if encountered
if (cssTransformError) throw cssTransformError;

// Compile `.ts` to `.js`
const { code, map } = await esbuild.transform(tsResult.code, { loader: 'ts', sourcemap: 'external', sourcefile: id });

Expand Down
7 changes: 7 additions & 0 deletions packages/astro/test/fixtures/sass/src/pages/error.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<style lang="scss">
$blue: blue;

.h1 {
color: $red; // undefined!
}
</style>
23 changes: 23 additions & 0 deletions packages/astro/test/sass.test.js
Original file line number Diff line number Diff line change
@@ -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');
});
});

0 comments on commit c491d1f

Please sign in to comment.