From df92aa863513cb7c3063d6f15cd8f0c721a3b685 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=86=9C=E6=9C=89=E5=AE=9D?= Date: Tue, 6 Jun 2023 19:57:33 +0800 Subject: [PATCH] feat: catch transform css error (#9) --- __tests__/css-error/index.css | 11 ++++++++ __tests__/css-error/index.jsx | 11 ++++++++ __tests__/css-error/index.test.ts | 19 +++++++++++++ src/index.ts | 46 +++++++++++++++++++++++-------- src/utils.ts | 5 ++++ 5 files changed, 80 insertions(+), 12 deletions(-) create mode 100644 __tests__/css-error/index.css create mode 100644 __tests__/css-error/index.jsx create mode 100644 __tests__/css-error/index.test.ts diff --git a/__tests__/css-error/index.css b/__tests__/css-error/index.css new file mode 100644 index 0000000..e4b67e7 --- /dev/null +++ b/__tests__/css-error/index.css @@ -0,0 +1,11 @@ +.app { + padding: 20px; + user-select: none; +} + +.title { + font-size: var(-- 15px, 15px); + color: #000; +} + + diff --git a/__tests__/css-error/index.jsx b/__tests__/css-error/index.jsx new file mode 100644 index 0000000..c2de796 --- /dev/null +++ b/__tests__/css-error/index.jsx @@ -0,0 +1,11 @@ +import styles from './index.css'; + +import React from 'react'; +import { createRoot } from 'react-dom/client'; +const App = () => ( +
+

Hello World

+
+); + +createRoot(document.getElementById('root')).render(); diff --git a/__tests__/css-error/index.test.ts b/__tests__/css-error/index.test.ts new file mode 100644 index 0000000..867191c --- /dev/null +++ b/__tests__/css-error/index.test.ts @@ -0,0 +1,19 @@ +import path from 'path'; +import { removeComments, runTest } from '../runTest'; +import fse from 'fs-extra'; +import { formatMessages } from 'esbuild'; + +const OUTPUT_HTML = !!process.env.OUTPUT_HTML; + +describe(path.basename(path.dirname(__filename)), function () { + it('style-loader', async function () { + const output = path.resolve(__dirname, 'output'); + fse.removeSync(output); + await runTest([path.resolve(__dirname, './index.jsx')], output).catch((error) => { + formatMessages(error.errors, { kind: 'error', color: false }).then((res) => { + expect(res.length).toBe(1); + expect(res[0]).toContain('[ERROR] Unexpected token Dimension'); + }); + }); + }); +}); diff --git a/src/index.ts b/src/index.ts index 5786a54..cff75b5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,13 +1,12 @@ -import { OnResolveArgs, Plugin } from 'esbuild'; +import { OnResolveArgs, PartialMessage, Plugin } from 'esbuild'; import PATH from 'path'; -import browserslist from 'browserslist'; import { CSSModulesConfig, transform } from 'lightningcss'; import { readFile } from 'fs/promises'; import qs from 'query-string'; import deepmerge from 'deepmerge'; import { transformLess } from './transform-less'; -import { codeWithSourceMap, cssExportsToJs, generateTargets, parsePath, resolvePath } from './utils'; +import { codeWithSourceMap, cssExportsToJs, generateTargets, parsePath, replaceExtension, resolvePath } from './utils'; import { convertLessError } from './less-utils'; import { transformSass } from './transform-sass'; import { TransformResult } from './types'; @@ -112,15 +111,38 @@ export const styleLoader = (options: StyleLoaderOptions = {}): Plugin => { }; } - const { code, map, exports } = transform({ - targets: targets, - inputSourceMap: cssSourceMap, - sourceMap: true, - filename: args.path, - cssModules: enableCssModules ? opts.cssModules : false, - code: Buffer.from(cssContent), - }); - // TODO: throw error if css is invalid + let transformResult; + + try { + transformResult = transform({ + targets: targets, + inputSourceMap: cssSourceMap, + sourceMap: true, + filename: args.path, + cssModules: enableCssModules ? opts.cssModules : false, + code: Buffer.from(cssContent), + }); + } catch (error) { + const { loc, fileName, source } = error; + const lines = source.split('\n'); + const lineText = lines[loc.line - 1]; + return { + errors: [ + { + text: error.message, + location: { + file: replaceExtension(fileName, '.css'), + line: loc.line, + column: loc.column, + lineText, + }, + } as PartialMessage, + ], + resolveDir: PATH.dirname(args.path), + }; + } + + const { code, map, exports } = transformResult; if (buildOptions.sourcemap && map) { cssContent = codeWithSourceMap(code.toString(), map.toString()); diff --git a/src/utils.ts b/src/utils.ts index 156ebec..cf44389 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -69,3 +69,8 @@ export async function resolvePath(args: OnResolveArgs, build: PluginBuild) { export const generateTargets = (queries: string) => { return browserslistToTargets(browserslist(queries)); }; + +export const replaceExtension = (file: string, ext: string) => { + const extName = PATH.extname(file); + return file.slice(0, file.length - extName.length) + ext; +};