Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changeset/fix-template-css-diagnostics-sourcemap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---

---
Comment thread
luhc228 marked this conversation as resolved.
Comment thread
luhc228 marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,9 @@ export function processTasmCSSDiagnostics(input: ProcessTasmCSSDiagnosticsOption

// @public
export interface ProcessTasmCSSDiagnosticsOptions {
compilation: Compilation;
context: string;
cssDiagnostics: unknown;
cssSourceMaps: string[];
emittedWarnings?: Set<string> | undefined;
fileExists?: ((path: string) => boolean) | undefined;
}
Expand Down Expand Up @@ -159,6 +159,7 @@ export interface TemplateHooks {
beforeEmit: AsyncSeriesWaterfallHook<{
finalEncodeOptions: EncodeOptions;
debugInfo: string;
cssDiagnostics?: string;
template: Buffer;
outputName: string;
mainThreadAssets: Asset[];
Expand All @@ -180,6 +181,7 @@ export interface TemplateHooks {
}, {
buffer: Buffer;
debugInfo: string;
cssDiagnostics?: string;
}>;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@

import type { Chunk, Compiler } from 'webpack';

import { processTasmCSSDiagnostics } from './cssDiagnostics.js';
import {
collectCSSSourceMapContents,
processTasmCSSDiagnostics,
} from './cssDiagnostics.js';
import { LynxTemplatePlugin } from './LynxTemplatePlugin.js';
import { getRequireModuleAsyncCachePolyfill } from './polyfill/requireModuleAsync.js';

Expand Down Expand Up @@ -234,9 +237,20 @@ export class LynxEncodePluginImpl {
encode(encodeOptions),
);

return {
buffer,
debugInfo: lepus_debug,
cssDiagnostics: css_diagnostics as string,
};
});

templateHooks.beforeEmit.tapPromise({
name: this.name,
stage: LynxEncodePlugin.BEFORE_EMIT_STAGE,
}, async (args) => {
const resolvedDiagnostics = processTasmCSSDiagnostics({
cssDiagnostics: css_diagnostics,
compilation,
cssDiagnostics: args.cssDiagnostics,
cssSourceMaps: collectCSSSourceMapContents(args.cssChunks),
Comment thread
luhc228 marked this conversation as resolved.
context: compiler.context,
emittedWarnings: emittedCSSDiagnosticWarnings,
});
Expand All @@ -245,7 +259,7 @@ export class LynxEncodePluginImpl {
const webpackWarning = new compiler.webpack.WebpackError(
diagnostic.message,
);
webpackWarning.stack = '';
webpackWarning.hideStack = true;

if (
diagnostic.sourceFile
Expand All @@ -272,7 +286,7 @@ export class LynxEncodePluginImpl {
});
}

return { buffer, debugInfo: lepus_debug };
return args;
});
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export interface TemplateHooks {
encodeOptions: EncodeOptions;
intermediate?: string;
},
{ buffer: Buffer; debugInfo: string }
{ buffer: Buffer; debugInfo: string; cssDiagnostics?: string }
>;

/**
Expand All @@ -123,6 +123,7 @@ export interface TemplateHooks {
beforeEmit: AsyncSeriesWaterfallHook<{
finalEncodeOptions: EncodeOptions;
debugInfo: string;
cssDiagnostics?: string;
template: Buffer;
outputName: string;
mainThreadAssets: Asset[];
Expand Down Expand Up @@ -912,7 +913,7 @@ class LynxTemplatePluginImpl {
}

try {
const { buffer, debugInfo } = await hooks.encode.promise({
const { buffer, debugInfo, cssDiagnostics } = await hooks.encode.promise({
encodeOptions: resolvedEncodeOptions,
intermediate,
});
Expand All @@ -926,6 +927,7 @@ class LynxTemplatePluginImpl {
const { template } = await hooks.beforeEmit.promise({
finalEncodeOptions: resolvedEncodeOptions,
debugInfo,
...(cssDiagnostics === undefined ? {} : { cssDiagnostics }),
template: buffer,
outputName: filename,
mainThreadAssets: [lepusCode.root, ...encodeData.lepusCode.chunks]
Expand Down
134 changes: 85 additions & 49 deletions packages/webpack/template-webpack-plugin/src/cssDiagnostics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { fileURLToPath } from 'node:url';

import { TraceMap, originalPositionFor } from '@jridgewell/trace-mapping';
import type { SourceMapInput } from '@jridgewell/trace-mapping';
import type { Compilation } from 'webpack';
import type { Asset } from 'webpack';

import type * as CSS from '@lynx-js/css-serializer';

Expand Down Expand Up @@ -72,9 +72,9 @@ export interface ProcessTasmCSSDiagnosticsOptions {
*/
cssDiagnostics: unknown;
/**
* The webpack compilation containing the main CSS asset.
* CSS source map contents from the CSS chunks for the current template.
*/
compilation: Compilation;
cssSourceMaps: string[];
/**
* The webpack compiler context used to resolve relative source paths.
*/
Expand All @@ -96,7 +96,7 @@ export interface ProcessTasmCSSDiagnosticsOptions {
*/
export function processTasmCSSDiagnostics({
cssDiagnostics,
compilation,
cssSourceMaps,
context,
emittedWarnings,
fileExists,
Expand All @@ -108,7 +108,7 @@ export function processTasmCSSDiagnostics({

const resolveOptions: Parameters<typeof resolveTasmCSSDiagnostics>[0] = {
cssDiagnostics: diagnostics,
mainCSSSourceMap: getMainCSSSourceMap(compilation),
mainCSSSourceMaps: parseCSSSourceMaps(cssSourceMaps),
context,
};
if (fileExists !== undefined) {
Expand All @@ -127,13 +127,13 @@ export function extractTasmCSSDiagnostics(value: unknown): TasmCSSDiagnostic[] {
}

try {
const parsed = JSON.parse(value) as unknown as TasmCSSDiagnostic[];
const parsed = JSON.parse(value) as unknown;
if (!Array.isArray(parsed)) {
return [];
}

return parsed
.map((element) => normalizeTasmCSSDiagnostic(element))
.map(element => normalizeTasmCSSDiagnostic(element))
.filter((diagnostic): diagnostic is TasmCSSDiagnostic => (
diagnostic !== null
));
Expand All @@ -144,60 +144,65 @@ export function extractTasmCSSDiagnostics(value: unknown): TasmCSSDiagnostic[] {

export function resolveTasmCSSDiagnostics({
cssDiagnostics,
mainCSSSourceMap,
mainCSSSourceMaps,
context,
fileExists = existsSync,
}: {
cssDiagnostics: TasmCSSDiagnostic[];
mainCSSSourceMap: CSS.CSSSourceMap | undefined;
mainCSSSourceMaps: CSS.CSSSourceMap[];
context: string;
fileExists?: (path: string) => boolean;
}): ResolvedTasmCSSDiagnostic[] {
if (!mainCSSSourceMap) {
if (mainCSSSourceMaps.length === 0) {
return cssDiagnostics.map(diagnostic => ({
...diagnostic,
message: formatTasmCSSDiagnosticMessage(diagnostic),
}));
}

const traceMap = new TraceMap(mainCSSSourceMap as SourceMapInput);
const traceMaps = mainCSSSourceMaps.map(sourceMap => ({
sourceMap,
traceMap: new TraceMap(sourceMap as SourceMapInput),
}));

return cssDiagnostics.map(diagnostic => {
const mapped = originalPositionFor(traceMap, {
line: diagnostic.line,
column: Math.max(diagnostic.column - 1, 0),
});

const message = formatTasmCSSDiagnosticMessage(diagnostic);
if (
mapped.source === null
|| mapped.line === null
|| mapped.column === null
) {
return {
...diagnostic,
message,
};
}

const sourceFile = normalizeTasmSourcePath(
mapped.source,
mainCSSSourceMap,
context,
);
if (!sourceFile || !fileExists(sourceFile)) {
for (const { sourceMap, traceMap } of traceMaps) {
const mapped = originalPositionFor(traceMap, {
line: diagnostic.line,
column: Math.max(diagnostic.column - 1, 0),
});

if (
mapped.source === null
|| mapped.line === null
|| mapped.column === null
) {
continue;
}

const sourceFile = normalizeTasmSourcePath(
mapped.source,
sourceMap,
context,
);
if (!sourceFile || !fileExists(sourceFile)) {
continue;
}

return {
...diagnostic,
message,
sourceFile,
sourceLine: mapped.line,
sourceColumn: mapped.column + 1,
};
}

return {
...diagnostic,
message,
sourceFile,
sourceLine: mapped.line,
sourceColumn: mapped.column + 1,
};
});
}
Expand Down Expand Up @@ -225,7 +230,43 @@ export function dedupeTasmCSSDiagnostics<T extends ResolvedTasmCSSDiagnostic>(
});
}

type Asset = ReturnType<Compilation['getAssets']>[number];
export function collectCSSSourceMapContents(
cssChunks: Asset[] | undefined,
): string[] {
const sourceMaps: string[] = [];

if (!cssChunks || cssChunks.length === 0) {
return sourceMaps;
}

cssChunks.forEach((cssChunk) => {
const sourceMap = normalizeCSSSourceMap(cssChunk.source.map?.());
if (sourceMap) {
sourceMaps.push(JSON.stringify(sourceMap));
}
});

return Array.from(new Set(sourceMaps));
}

function parseCSSSourceMaps(cssSourceMaps: string[]): CSS.CSSSourceMap[] {
return cssSourceMaps.reduce<CSS.CSSSourceMap[]>((parsed, cssSourceMap) => {
if (cssSourceMap.trim() === '') {
return parsed;
}

try {
const sourceMap = JSON.parse(cssSourceMap) as unknown;
if (isCSSSourceMap(sourceMap)) {
parsed.push(sourceMap);
}
} catch {
// Ignore invalid source map payloads.
}

return parsed;
}, []);
}

function normalizeCSSSourceMap(
sourceMap: ReturnType<Asset['source']['map']> | undefined,
Expand All @@ -237,21 +278,16 @@ function normalizeCSSSourceMap(
return sourceMap;
}

export function getMainCSSSourceMap(
compilation: Compilation,
): CSS.CSSSourceMap | undefined {
for (const asset of compilation.getAssets()) {
if (!asset.name.endsWith('.css')) {
continue;
}

const sourceMap = normalizeCSSSourceMap(asset.source.map?.());
if (sourceMap) {
return sourceMap;
}
function isCSSSourceMap(value: unknown): value is CSS.CSSSourceMap {
if (!isRecord(value)) {
return false;
}

return undefined;
return (
typeof value['version'] === 'number'
&& typeof value['mappings'] === 'string'
&& Array.isArray(value['sources'])
);
}

function normalizeTasmCSSDiagnostic(value: unknown): TasmCSSDiagnostic | null {
Expand Down
Loading
Loading