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
5 changes: 5 additions & 0 deletions .changeset/fix-css-id-source-map-offset.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@lynx-js/css-extract-webpack-plugin": patch
Comment thread
luhc228 marked this conversation as resolved.
---

Fix CSS source map line offsets when wrapping extracted CSS with cssId metadata.
35 changes: 28 additions & 7 deletions packages/webpack/css-extract-webpack-plugin/src/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,16 @@ type Dependency = [
id: string,
content: string,
media: string,
sourceMap: string | Buffer | undefined,
sourceMap: DependencySourceMap | undefined,
supports: string | undefined,
layer: string | undefined,
];

interface DependencySourceMap {
mappings?: string | undefined;
[key: string]: unknown;
}

/**
* With css-loader options: `{esModule: true}`
*/
Expand Down Expand Up @@ -178,6 +183,9 @@ export async function load(
this.rootContext,
extractPathFromIdentifier(identifier)!,
);
const shouldWrapCSSId = Boolean(cssId)
&& (params.get('common') === null
|| params.get('common') === 'false');

identifierCountMap.set(identifier, count + 1);

Expand All @@ -188,9 +196,7 @@ export async function load(
),
context: this.rootContext,
content: Buffer.from(
cssId
&& (params.get('common') === null
|| params.get('common') === 'false')
shouldWrapCSSId
/**
* Given the following source code:
*
Expand Down Expand Up @@ -222,8 +228,7 @@ export async function load(
* }
* ```
*/
? `\
@cssId "${cssId}" "${filePath}" {
? `@cssId "${cssId}" "${filePath}" {
${content}
}
`
Expand All @@ -234,7 +239,9 @@ ${content}
layer,
identifierIndex: count,
sourceMap: sourceMap
? Buffer.from(JSON.stringify(sourceMap))
? Buffer.from(JSON.stringify(
shouldWrapCSSId ? offsetSourceMapLines(sourceMap, 1) : sourceMap,
))
: undefined,
};
},
Expand Down Expand Up @@ -299,6 +306,20 @@ ${content}
return resultSource;
}

export function offsetSourceMapLines<T extends DependencySourceMap>(
sourceMap: T,
lineOffset: number,
): T {
if (lineOffset <= 0 || !sourceMap.mappings) {
return sourceMap;
}

return {
...sourceMap,
mappings: `${';'.repeat(lineOffset)}${sourceMap.mappings}`,
};
}

export async function pitch(
this: LoaderContext<LoaderOptions>,
request: string,
Expand Down
35 changes: 35 additions & 0 deletions packages/webpack/css-extract-webpack-plugin/test/loader.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright 2024 The Lynx Authors. All rights reserved.
// Licensed under the Apache License Version 2.0 that can be found in the
// LICENSE file in the root directory of this source tree.
import { describe, expect, test } from 'vitest';

import { offsetSourceMapLines } from '../src/loader.js';

describe('loader', () => {
test('offsets generated source map lines when cssId wraps CSS content', () => {
expect(
offsetSourceMapLines({
version: 3,
sources: ['index.ttss'],
names: [],
mappings: 'AAAA;AACA;AACA',
}, 1),
).toEqual({
version: 3,
sources: ['index.ttss'],
names: [],
mappings: ';AAAA;AACA;AACA',
});
});

test('keeps empty source maps unchanged', () => {
const sourceMap = {
version: 3,
sources: ['index.ttss'],
names: [],
mappings: '',
};

expect(offsetSourceMapLines(sourceMap, 1)).toBe(sourceMap);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,34 @@ export interface LynxTemplatePluginOptions {
targetSdkVersion: string;
}

// @public
export function processTasmCSSDiagnostics(input: ProcessTasmCSSDiagnosticsOptions): ResolvedTasmCSSDiagnostic[];

// @public
export interface ProcessTasmCSSDiagnosticsOptions {
compilation: Compilation;
context: string;
cssDiagnostics: unknown;
emittedWarnings?: Set<string> | undefined;
fileExists?: ((path: string) => boolean) | undefined;
}

// @public
export interface ResolvedTasmCSSDiagnostic extends TasmCSSDiagnostic {
message: string;
sourceColumn?: number | undefined;
sourceFile?: string | undefined;
sourceLine?: number | undefined;
}

// @public
export interface TasmCSSDiagnostic {
column: number;
line: number;
name?: string | undefined;
type?: string | undefined;
}

// @public
export interface TemplateHooks {
// @alpha
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,9 @@
// Licensed under the Apache License Version 2.0 that can be found in the
// LICENSE file in the root directory of this source tree.

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

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

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

Expand Down Expand Up @@ -240,17 +234,13 @@ export class LynxEncodePluginImpl {
encode(encodeOptions),
);

const diagnostics = extractTasmCSSDiagnostics(css_diagnostics);
if (diagnostics.length > 0) {
const resolvedDiagnostics = dedupeTasmCSSDiagnostics(
resolveTasmCSSDiagnostics({
cssDiagnostics: diagnostics,
mainCSSSourceMap: getMainCSSSourceMap(compilation),
context: compiler.context,
}),
emittedCSSDiagnosticWarnings,
);

const resolvedDiagnostics = processTasmCSSDiagnostics({
cssDiagnostics: css_diagnostics,
compilation,
context: compiler.context,
emittedWarnings: emittedCSSDiagnosticWarnings,
});
if (resolvedDiagnostics.length > 0) {
resolvedDiagnostics.forEach((diagnostic) => {
const webpackWarning = new compiler.webpack.WebpackError(
diagnostic.message,
Expand Down Expand Up @@ -374,35 +364,6 @@ export class LynxEncodePluginImpl {
protected options: Required<LynxEncodePluginOptions>;
}

type Asset = ReturnType<Compilation['getAssets']>[number];

function normalizeCSSSourceMap(
sourceMap: ReturnType<Asset['source']['map']> | undefined,
): CSS.CSSSourceMap | undefined {
if (!sourceMap || Array.isArray(sourceMap)) {
return undefined;
}

return sourceMap;
}

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;
}
}

return undefined;
}

export function isDebug(): boolean {
if (!process.env['DEBUG']) {
return false;
Expand Down
Loading
Loading