From 4a1c07dec39167e109a133a59bf572fbacceb61e Mon Sep 17 00:00:00 2001 From: Thierry Bela Nanga Date: Wed, 12 Aug 2026 09:41:00 -0400 Subject: [PATCH 01/22] update link to llms.txt --- src/lib/fs/resolve.ts | 7 +++---- typedoc.config.js | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/lib/fs/resolve.ts b/src/lib/fs/resolve.ts index e39004d8..08428858 100644 --- a/src/lib/fs/resolve.ts +++ b/src/lib/fs/resolve.ts @@ -149,10 +149,6 @@ export const resolve = memoize(function ( cwd?: string, ): { absolute: string; relative: string } { - - cwd ??= ""; - currentDirectory ??= ""; - if (matchUrl.test(url)) { return { absolute: url, @@ -160,6 +156,9 @@ export const resolve = memoize(function ( }; } + cwd ??= ""; + currentDirectory ??= ""; + url = normalize(url); diff --git a/typedoc.config.js b/typedoc.config.js index 2da9bb9f..43cb0480 100644 --- a/typedoc.config.js +++ b/typedoc.config.js @@ -9,7 +9,7 @@ export default { Benchmark: "https://tbela99.github.io/css-parser/benchmark/index.html", Docs: "https://tbela99.github.io/css-parser/docs/", Playground: "https://tbela99.github.io/css-parser/playground/", - "llm.txt": "https://github.com/tbela99/css-parser/llms.txt", + "llm.txt": "https://tbela99.github.io/css-parser/llms.txt", GitHub: "https://github.com/tbela99/css-parser", }, highlightLanguages: ["ts", "css", "javascript", "json", 'html', 'shell'], From d758f20530c1eaa7ecac47b567eb46f9f722e09b Mon Sep 17 00:00:00 2001 From: Thierry Bela Nanga Date: Wed, 12 Aug 2026 13:59:07 -0400 Subject: [PATCH 02/22] fix documentation --- dist/index-umd-web.js | 63 ++++++++++++++++++------------ dist/index.cjs | 63 ++++++++++++++++++------------ dist/index.d.ts | 16 ++++---- dist/lib/ast/clone.js | 16 ++++---- dist/lib/fs/resolve.js | 4 +- dist/lib/parser/declaration/map.js | 17 +++++--- dist/lib/parser/parse.js | 18 ++++++--- dist/node.js | 8 ++-- dist/web.js | 8 ++-- files/usage.md | 13 +++--- llms.txt | 6 +-- src/lib/ast/clone.ts | 16 ++++---- src/lib/parser/declaration/map.ts | 27 ++++++++----- src/lib/parser/parse.ts | 29 +++++++------- src/node.ts | 24 ++++++------ src/web.ts | 24 ++++++------ 16 files changed, 202 insertions(+), 150 deletions(-) diff --git a/dist/index-umd-web.js b/dist/index-umd-web.js index 1792fbfb..3148a197 100644 --- a/dist/index-umd-web.js +++ b/dist/index-umd-web.js @@ -11683,14 +11683,14 @@ clone[name] = value; } else if (Array.isArray(value)) { - clone[name] = - !cloneChildren && name == checkNode - ? [] - : value.map((c) => { - const newObj = cloneNode(c, cloneChildren, cloneMap); - cloneMap?.set?.(c, newObj); - return newObj; - }); + clone[name] = []; + if (cloneChildren || name !== checkNode) { + for (const c of value) { + const newObj = cloneNode(c, cloneChildren, cloneMap); + cloneMap?.set?.(c, newObj); + clone[name].push(newObj); + } + } } else { clone[name] = { ...value }; @@ -18912,12 +18912,19 @@ return acc; }, []); let isImportant = false; - const filtered = values.map(removeDefaults).filter((x) => x.val.filter((t) => { - if (t.typ == exports.EnumToken.ImportantTokenType) { - isImportant = true; + let dec; + const filtered = []; + for (const declaration of values) { + dec = removeDefaults(declaration); + for (const t of dec.val) { + if (t.typ == exports.EnumToken.ImportantTokenType) { + isImportant = true; + } + if (filtered.length == 0 && t.typ != exports.EnumToken.WhitespaceTokenType && t.typ != exports.EnumToken.ImportantTokenType) { + filtered.push(dec); + } } - return ![exports.EnumToken.WhitespaceTokenType, exports.EnumToken.ImportantTokenType].includes(t.typ); - }).length > 0); + } if (filtered.length == 0 && this.config.default.length > 0) { filtered.push({ typ: exports.EnumToken.DeclarationNodeType, @@ -24289,14 +24296,14 @@ * @private */ const resolve = memoize(function (url, currentDirectory, cwd) { - cwd ??= ""; - currentDirectory ??= ""; if (matchUrl.test(url)) { return { absolute: url, relative: url, }; } + cwd ??= ""; + currentDirectory ??= ""; url = normalize(url); if (currentDirectory !== "") { currentDirectory = normalize(currentDirectory); @@ -29316,12 +29323,13 @@ } if (moduleSettings.naming != exports.ModuleCaseTransformEnum.IgnoreCase) { revMapping = {}; - mapping = Object.entries(mapping).reduce((acc, [key, value]) => { - const keyName = getKeyName(key, moduleSettings.naming); - acc[keyName] = value; + mapping = {}; + let keyName; + for (const [key, value] of Object.entries(mapping)) { + keyName = getKeyName(key, moduleSettings.naming); + mapping[keyName] = value; revMapping[value] = keyName; - return acc; - }, {}); + } } result.mapping = mapping; result.revMapping = revMapping; @@ -31568,7 +31576,12 @@ position: 0, currentPosition: -1, }; - const result = parseTokens([...tokenize(parseInfo)].map((t) => t.token), options, errors); + const tokenResults = tokenize(parseInfo); + const mapped = []; + for (const token of tokenResults) { + mapped.push(token.token); + } + const result = parseTokens(mapped, options, errors); // remove EOF token result.pop(); if (result.at(-1)?.typ === exports.EnumToken.WhitespaceTokenType) { @@ -31925,10 +31938,10 @@ * * ```ts * - * import {parse} from '@tbela99/css-parser'; + * import {parseSync} from '@tbela99/css-parser'; * * // css string - * let result = await parse(css, {nestingRules: true}); + * let result = await parseSync(css, {nestingRules: true}); * console.log(result.ast); * ``` * @@ -31982,10 +31995,10 @@ * * ```ts * - * import {transform} from '@tbela99/css-parser'; + * import {transformSync} from '@tbela99/css-parser'; * * // css string - * const result = await transform(css); + * const result = transformSync(css); * console.log(result.code); * ``` * diff --git a/dist/index.cjs b/dist/index.cjs index b3a4b908..2f51323c 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -11686,14 +11686,14 @@ function cloneNode(node, cloneChildren = false, cloneMap = null) { clone[name] = value; } else if (Array.isArray(value)) { - clone[name] = - !cloneChildren && name == checkNode - ? [] - : value.map((c) => { - const newObj = cloneNode(c, cloneChildren, cloneMap); - cloneMap?.set?.(c, newObj); - return newObj; - }); + clone[name] = []; + if (cloneChildren || name !== checkNode) { + for (const c of value) { + const newObj = cloneNode(c, cloneChildren, cloneMap); + cloneMap?.set?.(c, newObj); + clone[name].push(newObj); + } + } } else { clone[name] = { ...value }; @@ -18915,12 +18915,19 @@ class PropertyMap { return acc; }, []); let isImportant = false; - const filtered = values.map(removeDefaults).filter((x) => x.val.filter((t) => { - if (t.typ == exports.EnumToken.ImportantTokenType) { - isImportant = true; + let dec; + const filtered = []; + for (const declaration of values) { + dec = removeDefaults(declaration); + for (const t of dec.val) { + if (t.typ == exports.EnumToken.ImportantTokenType) { + isImportant = true; + } + if (filtered.length == 0 && t.typ != exports.EnumToken.WhitespaceTokenType && t.typ != exports.EnumToken.ImportantTokenType) { + filtered.push(dec); + } } - return ![exports.EnumToken.WhitespaceTokenType, exports.EnumToken.ImportantTokenType].includes(t.typ); - }).length > 0); + } if (filtered.length == 0 && this.config.default.length > 0) { filtered.push({ typ: exports.EnumToken.DeclarationNodeType, @@ -24292,14 +24299,14 @@ const diff = memoize(function (path1, path2) { * @private */ const resolve = memoize(function (url, currentDirectory, cwd) { - cwd ??= ""; - currentDirectory ??= ""; if (matchUrl.test(url)) { return { absolute: url, relative: url, }; } + cwd ??= ""; + currentDirectory ??= ""; url = normalize(url); if (currentDirectory !== "") { currentDirectory = normalize(currentDirectory); @@ -29319,12 +29326,13 @@ function doParseSync(iter, options = {}) { } if (moduleSettings.naming != exports.ModuleCaseTransformEnum.IgnoreCase) { revMapping = {}; - mapping = Object.entries(mapping).reduce((acc, [key, value]) => { - const keyName = getKeyName(key, moduleSettings.naming); - acc[keyName] = value; + mapping = {}; + let keyName; + for (const [key, value] of Object.entries(mapping)) { + keyName = getKeyName(key, moduleSettings.naming); + mapping[keyName] = value; revMapping[value] = keyName; - return acc; - }, {}); + } } result.mapping = mapping; result.revMapping = revMapping; @@ -31571,7 +31579,12 @@ function parseString(src, options = { parseColor: true }, errors) { position: 0, currentPosition: -1, }; - const result = parseTokens([...tokenize(parseInfo)].map((t) => t.token), options, errors); + const tokenResults = tokenize(parseInfo); + const mapped = []; + for (const token of tokenResults) { + mapped.push(token.token); + } + const result = parseTokens(mapped, options, errors); // remove EOF token result.pop(); if (result.at(-1)?.typ === exports.EnumToken.WhitespaceTokenType) { @@ -31930,10 +31943,10 @@ const parseFile = node_util.deprecate(async (file, options = {}, asStream = fals * * ```ts * - * import {parse} from '@tbela99/css-parser'; + * import {parseSync} from '@tbela99/css-parser'; * * // css string - * let result = await parse(css, {nestingRules: true}); + * let result = parseSync(css, {nestingRules: true}); * console.log(result.ast); * ``` * @@ -31985,10 +31998,10 @@ function parseSync(...args) { * * ```ts * - * import {transform} from '@tbela99/css-parser'; + * import {transformSync} from '@tbela99/css-parser'; * * // css string - * const result = await transform(css); + * const result = transformSync(css); * console.log(result.code); * ``` * diff --git a/dist/index.d.ts b/dist/index.d.ts index 1eae9a4f..c60324b9 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -6074,10 +6074,10 @@ declare const parseFile: (file: string, options?: ParserOptions, asStream?: bool * * ```ts * - * import {parse} from '@tbela99/css-parser'; + * import {parseSync} from '@tbela99/css-parser'; * * // css string - * let result = await parse(css, {nestingRules: true}); + * let result = parseSync(css, {nestingRules: true}); * console.log(result.ast); * ``` * @@ -6092,10 +6092,10 @@ declare function parseSync(stream: string, options?: ParserSyncOptions): ParseRe * * ```ts * - * import {parse} from '@tbela99/css-parser'; + * import {parseSync} from '@tbela99/css-parser'; * * // css string - * let result = await parse({input: css, nestingRules: true}); + * let result = parseSync({input: css, nestingRules: true}); * console.log(result.ast); * ``` * @@ -6109,10 +6109,10 @@ declare function parseSync(options: ParseInputOptions & ParserSyncOptions): Pars * * ```ts * - * import {transform} from '@tbela99/css-parser'; + * import {transformSync} from '@tbela99/css-parser'; * * // css string - * const result = await transform(css, {beautify: true}); + * const result = transformSync(css, {beautify: true}); * console.log(result.code); * ``` * @@ -6124,10 +6124,10 @@ declare function transformSync(css: string, options?: TransformSyncOptions): Tra * * ```ts * - * import {transform} from '@tbela99/css-parser'; + * import {transformSync} from '@tbela99/css-parser'; * * // css string - * const result = await transform({input: css, beautify: true}); + * const result = transformSync({input: css, beautify: true}); * console.log(result.code); * ``` * diff --git a/dist/lib/ast/clone.js b/dist/lib/ast/clone.js index b5a3537a..3842583d 100644 --- a/dist/lib/ast/clone.js +++ b/dist/lib/ast/clone.js @@ -17,14 +17,14 @@ function cloneNode(node, cloneChildren = false, cloneMap = null) { clone[name] = value; } else if (Array.isArray(value)) { - clone[name] = - !cloneChildren && name == checkNode - ? [] - : value.map((c) => { - const newObj = cloneNode(c, cloneChildren, cloneMap); - cloneMap?.set?.(c, newObj); - return newObj; - }); + clone[name] = []; + if (cloneChildren || name !== checkNode) { + for (const c of value) { + const newObj = cloneNode(c, cloneChildren, cloneMap); + cloneMap?.set?.(c, newObj); + clone[name].push(newObj); + } + } } else { clone[name] = { ...value }; diff --git a/dist/lib/fs/resolve.js b/dist/lib/fs/resolve.js index 7a2d8b5a..9ae24cae 100644 --- a/dist/lib/fs/resolve.js +++ b/dist/lib/fs/resolve.js @@ -122,14 +122,14 @@ const diff = memoize(function (path1, path2) { * @private */ const resolve = memoize(function (url, currentDirectory, cwd) { - cwd ??= ""; - currentDirectory ??= ""; if (matchUrl.test(url)) { return { absolute: url, relative: url, }; } + cwd ??= ""; + currentDirectory ??= ""; url = normalize(url); if (currentDirectory !== "") { currentDirectory = normalize(currentDirectory); diff --git a/dist/lib/parser/declaration/map.js b/dist/lib/parser/declaration/map.js index 736cf11f..d1cfc62c 100644 --- a/dist/lib/parser/declaration/map.js +++ b/dist/lib/parser/declaration/map.js @@ -286,12 +286,19 @@ class PropertyMap { return acc; }, []); let isImportant = false; - const filtered = values.map(removeDefaults).filter((x) => x.val.filter((t) => { - if (t.typ == EnumToken.ImportantTokenType) { - isImportant = true; + let dec; + const filtered = []; + for (const declaration of values) { + dec = removeDefaults(declaration); + for (const t of dec.val) { + if (t.typ == EnumToken.ImportantTokenType) { + isImportant = true; + } + if (filtered.length == 0 && t.typ != EnumToken.WhitespaceTokenType && t.typ != EnumToken.ImportantTokenType) { + filtered.push(dec); + } } - return ![EnumToken.WhitespaceTokenType, EnumToken.ImportantTokenType].includes(t.typ); - }).length > 0); + } if (filtered.length == 0 && this.config.default.length > 0) { filtered.push({ typ: EnumToken.DeclarationNodeType, diff --git a/dist/lib/parser/parse.js b/dist/lib/parser/parse.js index 8325511f..cd5df1f8 100644 --- a/dist/lib/parser/parse.js +++ b/dist/lib/parser/parse.js @@ -1292,12 +1292,13 @@ function doParseSync(iter, options = {}) { } if (moduleSettings.naming != ModuleCaseTransformEnum.IgnoreCase) { revMapping = {}; - mapping = Object.entries(mapping).reduce((acc, [key, value]) => { - const keyName = getKeyName(key, moduleSettings.naming); - acc[keyName] = value; + mapping = {}; + let keyName; + for (const [key, value] of Object.entries(mapping)) { + keyName = getKeyName(key, moduleSettings.naming); + mapping[keyName] = value; revMapping[value] = keyName; - return acc; - }, {}); + } } result.mapping = mapping; result.revMapping = revMapping; @@ -3544,7 +3545,12 @@ function parseString(src, options = { parseColor: true }, errors) { position: 0, currentPosition: -1, }; - const result = parseTokens([...tokenize(parseInfo)].map((t) => t.token), options, errors); + const tokenResults = tokenize(parseInfo); + const mapped = []; + for (const token of tokenResults) { + mapped.push(token.token); + } + const result = parseTokens(mapped, options, errors); // remove EOF token result.pop(); if (result.at(-1)?.typ === EnumToken.WhitespaceTokenType) { diff --git a/dist/node.js b/dist/node.js index 47ce863a..21d0e975 100644 --- a/dist/node.js +++ b/dist/node.js @@ -141,10 +141,10 @@ const parseFile = deprecate(async (file, options = {}, asStream = false) => pars * * ```ts * - * import {parse} from '@tbela99/css-parser'; + * import {parseSync} from '@tbela99/css-parser'; * * // css string - * let result = await parse(css, {nestingRules: true}); + * let result = parseSync(css, {nestingRules: true}); * console.log(result.ast); * ``` * @@ -196,10 +196,10 @@ function parseSync(...args) { * * ```ts * - * import {transform} from '@tbela99/css-parser'; + * import {transformSync} from '@tbela99/css-parser'; * * // css string - * const result = await transform(css); + * const result = transformSync(css); * console.log(result.code); * ``` * diff --git a/dist/web.js b/dist/web.js index a6194266..63d9f568 100644 --- a/dist/web.js +++ b/dist/web.js @@ -133,10 +133,10 @@ async function parseFile(file, options = {}, asStream = false) { * * ```ts * - * import {parse} from '@tbela99/css-parser'; + * import {parseSync} from '@tbela99/css-parser'; * * // css string - * let result = await parse(css, {nestingRules: true}); + * let result = await parseSync(css, {nestingRules: true}); * console.log(result.ast); * ``` * @@ -190,10 +190,10 @@ function parseSync(...args) { * * ```ts * - * import {transform} from '@tbela99/css-parser'; + * import {transformSync} from '@tbela99/css-parser'; * * // css string - * const result = await transform(css); + * const result = transformSync(css); * console.log(result.code); * ``` * diff --git a/files/usage.md b/files/usage.md index b5a603b3..ffbc6107 100644 --- a/files/usage.md +++ b/files/usage.md @@ -17,9 +17,9 @@ The **synchronous API** is marginally faster than the asynchronous API, but it c | `transformSync()` | ✅ | ❌ | ✅ | | `render()` | ❌ | ❌ | ✅ | -> **Note:** `parse()` and `parseSync()` only produce the AST and does not generate CSS output. +> **Note:** `parse()` and `parseSync()` only produce the AST and do not generate CSS output. -By contrast, `transform()` and `transformSync()` parses the CSS **and** generates the transformed CSS text. This is useful when you want the rendered CSS directly without performing a separate AST rendering step. +By contrast, `transform()` and `transformSync()` parse the CSS **and** generate the transformed CSS text. This is useful when you want the rendered CSS directly without performing a separate AST rendering step. ### Usage @@ -53,18 +53,19 @@ console.debug(result.stats); Parsing converts input CSS into an **AST (Abstract Syntax Tree)**. -You can parse CSS in two ways: +You can parse CSS in multiple ways: -- `parse()` – Parses CSS and returns an AST. -- `transform()` – Parses and generate CSS as part of the transformation process. +- `parse()` or `parseSync()` – Parse CSS and returns an AST. +- `transform()` or `transformSync()` – Parse and generate CSS as part of the transformation process. For more information about the available parsing options, see the TypeScript documentation for [`ParserOptions`](../interfaces/node.ParserOptions.html). ### Usage -```javascript +```ts parse(css, parserOptions = {}) parse(parserOptions = {input: css}) +parse(parserOptions = {file: url_or_path}) ``` ### Example diff --git a/llms.txt b/llms.txt index ddef377c..50cb7811 100644 --- a/llms.txt +++ b/llms.txt @@ -43,15 +43,15 @@ console.log(result.code); ``` ## Important behavior notes -- parse() and transform() are lenient by default and preserve unknown constructs unless configured otherwise. +- parse(), parseSync(), transform() and transformSync() are lenient by default and preserve unknown constructs unless configured otherwise. - Comments are removed by default; preserve them with removeComments: false or preserveLicense: true. - Validation errors are available through the parse/transform result and through node.state and node.errors. - The library prioritizes compact output while preserving semantics. ## Useful concepts -- parse() returns AST, errors, and stats. +- parse(), parseSync() return AST, errors, and stats. - render() turns an AST into CSS text. -- transform() performs parsing and rendering together. +- transform(), transformSync() perform parsing and rendering together. - AST node types include StyleSheet, Rule, AtRule, Declaration, Comment, and Keyframes variants. ## Documentation files diff --git a/src/lib/ast/clone.ts b/src/lib/ast/clone.ts index 9389a67e..cba96bb2 100644 --- a/src/lib/ast/clone.ts +++ b/src/lib/ast/clone.ts @@ -24,15 +24,17 @@ export function cloneNode( if (value == null || typeof value != "object") { clone[name] = value; } else if (Array.isArray(value)) { - clone[name] = - !cloneChildren && name == checkNode - ? [] - : value.map((c) => { - const newObj = cloneNode(c, cloneChildren, cloneMap); + clone[name] = []; + if (cloneChildren || name !== checkNode) { + + for (const c of value) { + const newObj = cloneNode(c, cloneChildren, cloneMap); cloneMap?.set?.(c, newObj); - return newObj; - }); + clone[name].push(newObj); + } + } + } else { clone[name] = { ...value }; } diff --git a/src/lib/parser/declaration/map.ts b/src/lib/parser/declaration/map.ts index e6140a44..23f02650 100644 --- a/src/lib/parser/declaration/map.ts +++ b/src/lib/parser/declaration/map.ts @@ -382,16 +382,25 @@ export class PropertyMap { ); let isImportant: boolean = false; - const filtered: AstDeclaration[] = values.map(removeDefaults).filter( - (x: AstDeclaration): boolean => - x.val.filter((t: Token) => { - if (t.typ == EnumToken.ImportantTokenType) { - isImportant = true; - } + let dec: AstDeclaration; - return ![EnumToken.WhitespaceTokenType, EnumToken.ImportantTokenType].includes(t.typ); - }).length > 0, - ); + const filtered: AstDeclaration[] = []; + + for (const declaration of values) { + + dec = removeDefaults(declaration); + + for (const t of dec.val) { + if (t.typ == EnumToken.ImportantTokenType) { + isImportant = true; + } + + if (filtered.length == 0 && t.typ != EnumToken.WhitespaceTokenType && t.typ != EnumToken.ImportantTokenType) { + + filtered.push(dec); + } + } + } if (filtered.length == 0 && this.config.default.length > 0) { filtered.push({ diff --git a/src/lib/parser/parse.ts b/src/lib/parser/parse.ts index d62791a9..2285cb55 100644 --- a/src/lib/parser/parse.ts +++ b/src/lib/parser/parse.ts @@ -1742,17 +1742,15 @@ export function doParseSync( if (moduleSettings.naming != ModuleCaseTransformEnum.IgnoreCase) { revMapping = {}; - mapping = Object.entries(mapping).reduce( - (acc: Record, [key, value]: [string, string]) => { - const keyName = getKeyName(key, moduleSettings.naming!); + mapping = {} as Record; + let keyName: string; - acc[keyName] = value; - revMapping[value] = keyName; + for (const [key, value] of Object.entries(mapping)) { + keyName = getKeyName(key, moduleSettings.naming!); - return acc; - }, - {} as Record, - ); + mapping[keyName] = value; + revMapping[value] = keyName; + } } result.mapping = mapping; @@ -4616,11 +4614,14 @@ export function parseString( currentPosition: -1, }; - const result = parseTokens( - [...tokenize(parseInfo)].map((t) => t.token), - options, - errors, - ); + const tokenResults = tokenize(parseInfo); + const mapped = []; + + for (const token of tokenResults) { + mapped.push(token.token); + } + + const result = parseTokens(mapped, options, errors); // remove EOF token result.pop(); diff --git a/src/node.ts b/src/node.ts index 401b4403..b762c3bd 100644 --- a/src/node.ts +++ b/src/node.ts @@ -217,10 +217,10 @@ export const parseFile = deprecate( * * ```ts * - * import {parse} from '@tbela99/css-parser'; + * import {parseSync} from '@tbela99/css-parser'; * * // css string - * let result = await parse(css, {nestingRules: true}); + * let result = parseSync(css, {nestingRules: true}); * console.log(result.ast); * ``` * @@ -237,10 +237,10 @@ export function parseSync(stream: string, options?: ParserSyncOptions): ParseRes * * ```ts * - * import {parse} from '@tbela99/css-parser'; + * import {parseSync} from '@tbela99/css-parser'; * * // css string - * let result = await parse({input: css, nestingRules: true}); + * let result = parseSync({input: css, nestingRules: true}); * console.log(result.ast); * ``` * @@ -256,10 +256,10 @@ export function parseSync(options: ParseInputOptions & ParserSyncOptions): Parse * * ```ts * - * import {parse} from '@tbela99/css-parser'; + * import {parseSync} from '@tbela99/css-parser'; * * // css string - * let result = await parse(css, {nestingRules: true}); + * let result = parseSync(css, {nestingRules: true}); * console.log(result.ast); * ``` * @@ -324,10 +324,10 @@ export function parseSync( * * ```ts * - * import {transform} from '@tbela99/css-parser'; + * import {transformSync} from '@tbela99/css-parser'; * * // css string - * const result = await transform(css, {beautify: true}); + * const result = transformSync(css, {beautify: true}); * console.log(result.code); * ``` * @@ -340,10 +340,10 @@ export function transformSync(css: string, options?: TransformSyncOptions): Tran * * ```ts * - * import {transform} from '@tbela99/css-parser'; + * import {transformSync} from '@tbela99/css-parser'; * * // css string - * const result = await transform({input: css, beautify: true}); + * const result = transformSync({input: css, beautify: true}); * console.log(result.code); * ``` * @@ -357,10 +357,10 @@ export function transformSync(options: ParseInputOptions & TransformSyncOptions) * * ```ts * - * import {transform} from '@tbela99/css-parser'; + * import {transformSync} from '@tbela99/css-parser'; * * // css string - * const result = await transform(css); + * const result = transformSync(css); * console.log(result.code); * ``` * diff --git a/src/web.ts b/src/web.ts index 059a280e..4f961d59 100644 --- a/src/web.ts +++ b/src/web.ts @@ -207,10 +207,10 @@ export async function parseFile( * * ```ts * - * import {parse} from '@tbela99/css-parser'; + * import {parseSync} from '@tbela99/css-parser'; * * // css string - * let result = await parse(css, {nestingRules: true}); + * let result = await parseSync(css, {nestingRules: true}); * console.log(result.ast); * ``` * @@ -227,10 +227,10 @@ export function parseSync(stream: string, options?: ParserSyncOptions): ParseRes * * ```ts * - * import {parse} from '@tbela99/css-parser'; + * import {parseSync} from '@tbela99/css-parser'; * * // css string - * let result = await parse({input: css, nestingRules: true}); + * let result = await parseSync({input: css, nestingRules: true}); * console.log(result.ast); * ``` * @@ -246,10 +246,10 @@ export function parseSync(options: ParseInputOptions & ParserSyncOptions): Parse * * ```ts * - * import {parse} from '@tbela99/css-parser'; + * import {parseSync} from '@tbela99/css-parser'; * * // css string - * let result = await parse(css, {nestingRules: true}); + * let result = await parseSync(css, {nestingRules: true}); * console.log(result.ast); * ``` * @@ -317,10 +317,10 @@ export function parseSync( * * ```ts * - * import {transform} from '@tbela99/css-parser'; + * import {transformSync} from '@tbela99/css-parser'; * * // css string - * const result = await transform(css, {beautify: true}); + * const result = transformSync(css, {beautify: true}); * console.log(result.code); * ``` * @@ -333,10 +333,10 @@ export function transformSync(css: string, options?: TransformSyncOptions): Tran * * ```ts * - * import {transform} from '@tbela99/css-parser'; + * import {transformSync} from '@tbela99/css-parser'; * * // css string - * const result = await transform({input: css, beautify: true}); + * const result = transformSync({input: css, beautify: true}); * console.log(result.code); * ``` * @@ -350,10 +350,10 @@ export function transformSync(options: ParseInputOptions & TransformSyncOptions) * * ```ts * - * import {transform} from '@tbela99/css-parser'; + * import {transformSync} from '@tbela99/css-parser'; * * // css string - * const result = await transform(css); + * const result = transformSync(css); * console.log(result.code); * ``` * From 1d419ab634bff648c6dd05b4edcd68bfa1521571 Mon Sep 17 00:00:00 2001 From: Thierry Bela Nanga Date: Sat, 15 Aug 2026 10:19:26 -0400 Subject: [PATCH 03/22] implement inputSourceMap support #146 --- CHANGELOG.md | 6 + README.md | 8 +- dist/index-umd-web.js | 947 ++++++++++++------ dist/index.cjs | 944 +++++++++++------ dist/index.d.ts | 111 +- dist/lib/ast/expand.js | 5 +- dist/lib/ast/find.js | 2 +- dist/lib/ast/minify.js | 36 +- dist/lib/fs/resolve.js | 84 +- dist/lib/parser/linesmap.js | 4 +- dist/lib/parser/parse.js | 72 +- dist/lib/parser/source.js | 18 +- dist/lib/parser/tokenize.js | 8 +- dist/lib/renderer/render.js | 205 ++-- dist/lib/renderer/sourcemap/lib/codec.js | 78 ++ dist/lib/renderer/sourcemap/lib/encode.js | 37 - dist/lib/renderer/sourcemap/sourcemap.js | 197 +++- dist/lib/validation/match.js | 2 +- dist/node.js | 19 +- dist/utils.d.ts | 9 + dist/utils.js | 49 + dist/web.js | 22 +- files/getting-started.md | 8 +- files/transform.md | 24 + llms.txt | 11 +- src/@types/index.d.ts | 32 +- src/lib/ast/expand.ts | 21 +- src/lib/ast/find.ts | 12 +- src/lib/ast/minify.ts | 70 +- src/lib/fs/resolve.ts | 101 +- src/lib/parser/linesmap.ts | 5 +- src/lib/parser/parse.ts | 94 +- src/lib/parser/source.ts | 58 +- src/lib/parser/tokenize.ts | 13 +- src/lib/renderer/render.ts | 261 +++-- .../sourcemap/lib/{encode.ts => codec.ts} | 50 +- src/lib/renderer/sourcemap/sourcemap.ts | 253 ++++- src/lib/validation/match.ts | 2 +- src/node.ts | 31 +- src/utils.ts | 54 + src/web.ts | 28 +- test/specs/code/block.js | 16 +- test/specs/code/import1.js | 3 +- test/specs/code/modules.js | 16 +- test/specs/code/sourcemaps.js | 50 +- test/specs/code/validation.js | 23 +- 46 files changed, 2796 insertions(+), 1303 deletions(-) create mode 100644 dist/lib/renderer/sourcemap/lib/codec.js delete mode 100644 dist/lib/renderer/sourcemap/lib/encode.js create mode 100644 dist/utils.d.ts create mode 100644 dist/utils.js rename src/lib/renderer/sourcemap/lib/{encode.ts => codec.ts} (52%) create mode 100644 src/utils.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 715aba87..cf61c73f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +# v1.5.0 + +## Improvements + +- [x] Add support for input sourcemap + # v1.4.11 - fix bug in url resolution diff --git a/README.md b/README.md index 65508945..8833e01f 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,9 @@ # css-parser -CSS parser, transformer, minifier and validator for node and the browser +An all-in-one CSS parsing solution for Node.js and the browser, covering parsing, validation, transformation, minification, and AST-based tooling. + +The library always fully parses the stylesheet into a structured AST, and token values are exposed as typed data so custom transforms, plugins, and analysis can work with reliable, semantic input instead of raw strings. ## Installation @@ -23,9 +25,11 @@ $ deno add @tbela99/css-parser ## Features * **Zero dependencies** — lightweight and easy to integrate into any project. +* **All-in-one CSS parsing solution** covering parsing, validation, transformation, minification, and AST manipulation. * **Standards-based CSS validation** powered by MDN data. * **Full CSS Modules support** for modern component-based workflows. -* **Fault-tolerant parsing** that follows the CSS Syntax Module Level 3 specification. +* **Fault-tolerant parsing** that follows the CSS Syntax Module Level 3 specification and always produces a complete, structured parse result. +* **Typed tokens and AST** — parsed CSS is exposed as strongly typed tokens and nodes so plugins and transforms can operate on semantic structures. * **High-performance minification** with safe optimizations and no unsafe transforms. * **Advanced color processing** with support for modern color spaces and functions, including `color()`, `lab()`, `lch()`, `oklab()`, `oklch()`, `color-mix()`, `light-dark()`, system colors, and relative colors. * **Color conversion engine** capable of transforming colors between all supported formats. diff --git a/dist/index-umd-web.js b/dist/index-umd-web.js index 3148a197..c71e7413 100644 --- a/dist/index-umd-web.js +++ b/dist/index-umd-web.js @@ -12666,7 +12666,7 @@ if (syntaxes[i].isList) { result = matchListSyntax(syntaxes[i], context.slice(), options); if (result.success) { - options.visited.get(token).delete(syntaxes[i]); + options.visited.get(token)?.delete?.(syntaxes[i]); if (result.context.done()) { context.end(); return { @@ -21050,7 +21050,7 @@ } /** - * Search the ast sub-tree and return the first match + * Search the ast subtree and return the first match * * ```ts * // find the first ast declaration node which name is 'aspect-ratio' @@ -21389,6 +21389,315 @@ TransformCssFeature: TransformCssFeature }); + // from https://github.com/Rich-Harris/vlq/tree/master + // credit: Rich Harris + const integer_to_char = {}; + const char_to_integer = {}; + let i = 0; + for (const char of 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=') { + char_to_integer[char] = i; + integer_to_char[i++] = char; + } + /** + * @param {string} str + */ + function decode(str) { + /** @type {number[]} */ + let result = []; + let shift = 0; + let value = 0; + for (let i = 0; i < str.length; i += 1) { + let integer = char_to_integer[str[i]]; + if (integer === undefined) { + throw new Error('Invalid character (' + str[i] + ')'); + } + const has_continuation_bit = integer & 32; + integer &= 31; + value += integer << shift; + if (has_continuation_bit) { + shift += 5; + } + else { + const should_negate = value & 1; + value >>>= 1; + if (should_negate) { + result.push(value === 0 ? -2147483648 : -value); + } + else { + result.push(value); + } + // reset + value = shift = 0; + } + } + return result; + } + /** + * + * @param value + * @returns + */ + function encode(value) { + if (typeof value === 'number') { + return encode_integer(value); + } + let result = ''; + for (let i = 0; i < value.length; i += 1) { + result += encode_integer(value[i]); + } + return result; + } + function encode_integer(num) { + let result = ''; + if (num < 0) { + num = (-num << 1) | 1; + } + else { + num <<= 1; + } + do { + let clamped = num & 31; + num >>>= 5; + if (num > 0) { + clamped |= 32; + } + result += integer_to_char[clamped]; + } while (num > 0); + return result; + } + + /** + * Generate and parse source map + */ + class SourceMap { + /** + * + * @private + */ + keys = new Set(); + /** + * Last location + */ + lastLocation = null; + /** + * Version + * @private + */ + version = 3; + /** + * Sources map + * @private + */ + sourcesMap = []; + /** + * Sources content + * @private + */ + sourcesContent = []; + /** + * Sources + * @private + */ + sources = []; + /** + * Map + * @private + * + */ + map = new Map(); + /** + * Map + * @private + * + */ + reverseMap = new Map(); + /** + * Line + * @private + */ + line = -1; + /** + * + * @param sourcemaps + */ + constructor(sourcemaps) { + if (typeof sourcemaps === "string") { + sourcemaps = JSON.parse(sourcemaps); + } + if (sourcemaps != null) { + this.sources = sourcemaps.sources?.slice() ?? []; + this.sourcesContent = sourcemaps.sourcesContent?.slice() ?? []; + const decodedMappings = sourcemaps.mappings + .split(";") + .map((mapping) => mapping.split(",").map((mapping) => decode(mapping))); + this.line = decodedMappings.length - 1; + for (let index = 0; index < decodedMappings.length; index++) { + if (decodedMappings[index].length == 0 || + (decodedMappings[index].length == 1 && decodedMappings[index][0].length == 0)) { + continue; + } + this.map.set(index, decodedMappings[index]); + } + this.computePositions(); + } + } + /** + * Add all location + * @param maps + */ + addAll(maps) { + for (let [newLine, newColumn, srcId, ln, col, sourceFileName, sourceContent] of maps) { + const key = `${srcId}:${ln}:${sourceFileName}:${col}:${newLine}:${newColumn}:${sourceContent}`; + const sourcemap = `${srcId}:${sourceFileName}:${sourceContent}`; + if (this.keys.has(key)) { + continue; + } + this.keys.add(key); + if (!this.sourcesMap.includes(sourcemap)) { + this.sourcesMap.push(sourcemap); + this.sources.push(sourceFileName || null); + this.sourcesContent.push((sourceFileName != null ? null : sourceContent) || null); + } + const line = newLine - 1; + let record; + if (line > this.line) { + this.line = line; + } + if (!this.map.has(line)) { + record = [Math.max(0, newColumn - 1), this.sourcesMap.indexOf(sourcemap), ln - 1, col - 1]; + this.map.set(line, [record]); + } + else { + const arr = this.map.get(line); + record = [ + Math.max(0, newColumn - 1) - arr[0][0], + this.sourcesMap.indexOf(sourcemap) - arr[0][1], + ln - 1, + col - 1, + ]; + arr.push(record); + } + if (this.lastLocation != null) { + record[2] -= this.lastLocation.ln - 1; + record[3] -= this.lastLocation.col - 1; + } + this.lastLocation ??= { ln, col }; + this.lastLocation.ln = ln; + this.lastLocation.col = col; + } + } + /** + * compute original positions + */ + computePositions() { + this.reverseMap.clear(); + let sourceFileIndex = 0; // second field + let sourceCodeLine = 0; // third field + let sourceCodeColumn = 0; // fourth field + let nameIndex = 0; // fifth field + let generatedCodeColumn; + let result; + // mappings to original source + for (let [i, line] of this.map.entries()) { + if (line.length === 0 || (line.length === 1 && line[0].length === 0)) { + continue; + } + generatedCodeColumn = line[0][0]; // first field - reset each time + line = line + .map((segment, index, array) => { + if (segment.length === 0) { + return []; + } + generatedCodeColumn = index == 0 ? segment[0] : segment[0] + array[0][0]; + result = [generatedCodeColumn]; + if (segment.length <= 1) { + return result; + } + sourceFileIndex = index == 0 ? segment[1] : segment[1] + array[0][1]; + sourceCodeLine += segment[2]; + sourceCodeColumn += segment[3]; + result.push(sourceFileIndex, sourceCodeLine, sourceCodeColumn); + if (segment.length === 5) { + nameIndex += segment[4]; + result.push(nameIndex); + } + return result; + }) + .sort((a, b) => { + if (a[1] !== b[1]) { + return a[1] - b[1]; + } + return a[0] - b[0]; + }); + if (line.length == 0 || (line.length == 1 && line[0].length == 0)) { + continue; + } + this.reverseMap.set(i, line); + } + } + /** + * retrieve original sources, lines and columns + * @param line generated line + * @param column generated column + */ + find(line, column) { + if (!this.reverseMap.has(--line)) { + return null; + } + column--; + const result = []; + for (const record of this.reverseMap.get(line)) { + if (record.length == 0 || record[0] < column) { + continue; + } + if (record[0] > column) { + break; + } + result.push([ + this.sources?.[record[1]] ?? null, + record[2] + 1, + record[3] + 1, + this.sourcesContent?.[record[1]] ?? null, + ]); + } + return result.length == 0 ? null : result; + } + /** + * Convert to URL encoded string + */ + toUrl() { + // /*# sourceMappingURL = ${url} */ + return `data:application/json;charset=utf-8;base64,${btoa(JSON.stringify(this.toJSON()))}`; + } + /** + * Convert to JSON object + */ + toJSON() { + const mappings = []; + let i = 0; + for (; i <= this.line; i++) { + if (!this.map.has(i)) { + mappings.push(""); + } + else { + mappings.push(this.map.get(i).reduce((acc, curr) => acc + (acc === "" ? "" : ",") + encode(curr), "")); + } + } + return { + version: this.version, + sources: this.sources.slice(), + sourcesContent: this.sourcesContent?.slice(), + mappings: mappings.join(";"), + }; + } + /** + * to string + */ + toString() { + return JSON.stringify(this); + } + } + /** * Compute line and column of the offset */ @@ -21401,7 +21710,7 @@ * Constructor * @param lines */ - constructor(lines) { + constructor(lines = []) { if (lines.length === 0) { lines.push(0); } @@ -21419,7 +21728,7 @@ } const column = offset - this.lineStarts[line]; // [line, column] - return [line + 1, column === 0 ? 1 : column]; + return [line + 1, line === 0 ? column + 1 : column]; } /** * search the greatest index of the value less than or equal to offset @@ -21474,6 +21783,7 @@ * Source file helper class */ class SourceFile { + inputSourceMap = null; /** * Source file ID */ @@ -21492,7 +21802,6 @@ content; /** * Constructor - * @param id * @param content * @param lines * @param file @@ -21506,7 +21815,6 @@ /** * Update source content * @param content - * @param lines */ append(content) { this.content += content; @@ -21564,6 +21872,20 @@ addLineStart(lineStart) { this.lineStarts.addLineStart(lineStart); } + /** + * set input source map + * @param inputSourceMap + */ + setInputSourceMap(inputSourceMap) { + this.inputSourceMap = inputSourceMap == null ? null : new SourceMap(inputSourceMap); + } + /** + * return input source map + * @returns + */ + getInputSourceMap() { + return this.inputSourceMap; + } } const SymbolsMapTokens = { @@ -21940,7 +22262,7 @@ return char; } /** - * Tokenize css string + * Tokenize CSS string * @param parseInfo * @param yieldEOFToken */ @@ -21967,8 +22289,6 @@ parseInfo.buffer = ""; while ((value = peek(parseInfo))) { charCode = value.charCodeAt(0); - // nextCharCode = nextValue.charCodeAt(0); - // console.debug({value, buffer}); switch (charCode) { case 61 /* TokenMap.EQUALS */: if (buffer.length > 0) { @@ -22333,10 +22653,6 @@ break; } buffer += value + next(parseInfo); - // buffer += - // (parseInfo.offset == parseInfo.currentPosition - // ? parseInfo.buffer.slice(-1) - // : parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset - 1)) + value; break; case 39 /* TokenMap.SINGLE_QUOTE */: case 34 /* TokenMap.DOUBLE_QUOTE */: @@ -22423,13 +22739,16 @@ * @param errors * @param nestingContent * + * @param context * @private */ - function minify(ast, options = {}, recursive = false, errors, nestingContent, context = {}) { + function minify(ast, opt = {}, recursive = false, errors, nestingContent, context = {}) { let preprocess = false; let postprocess = false; let parents; let replacement; + // @ts-ignore + let { sourcemap, module, ...options } = opt; if (!("features" in options)) { // @ts-ignore options = { @@ -22626,9 +22945,9 @@ * Minify at-rule media * - remove redundant tokens * - generate range queries - * @param ast * * @private + * @param tokens */ function minifyAtRuleMedia(tokens) { let hasUpdates = false; @@ -22726,7 +23045,6 @@ } while (previous?.typ === exports.EnumToken.CommentNodeType) { previous = ast.chi[--nodeIndex]; - continue; } node = ast.chi[i]; if (node.typ === exports.EnumToken.AtRuleNodeType && node.nam === "font-face") { @@ -23197,7 +23515,9 @@ } break; } - selector.forEach((selector) => selector.splice(0, optimized.length)); + for (let i1 = 0; i1 < selector.length; i1++) { + selector[i1].splice(0, optimized.length); + } let reducible = optimized.length == 1; if (optimized[0] == "&") { if (optimized[1] == " ") { @@ -23579,7 +23899,6 @@ * Diff nodes * @param n1 * @param n2 - * @param reducer * @param options * * @private @@ -23698,17 +24017,36 @@ // @ts-ignore chi: intersect.reverse(), }; + let op = { level: 0, ...options }; if (result == null || [n1, n2].reduce((acc, curr) => { let css = options.cache.get(curr); if (css == null) { - css = doRender(curr, options).code; + let level = 0; + let parent = curr[PARENT]; + while (parent != null && parent.typ != exports.EnumToken.StyleSheetNodeType) { + level++; + parent = parent[PARENT]; + } + op.level = level; + css = doRender(curr, op).code; options.cache.set(curr, css); } return curr.chi.length == 0 ? acc : acc + css.length; }, 0) <= [node1, node2, result].reduce((acc, curr) => { - const css = doRender(curr, options).code; + let css = options.cache.get(curr); + if (css != null) { + return curr.chi.length == 0 ? acc : acc + css.length; + } + let level = 0; + let parent = curr[PARENT]; + while (parent != null && parent.typ != exports.EnumToken.StyleSheetNodeType) { + level++; + parent = parent[PARENT]; + } + op.level = level; + css = doRender(curr, op).code; return curr.chi.length == 0 ? acc : acc + css.length; }, 0)) { if (node1.chi.length != 0 && node2.chi.length != 0) { @@ -23843,7 +24181,10 @@ ast.chi.splice(i--, 1); continue; } - selRule.forEach((arr) => combinators.includes(arr[0].charAt(0)) ? arr.unshift(arSelf) : arr.unshift(arSelf, " ")); + for (let i1 = 0; i1 < selRule.length; i1++) { + const arr = selRule[i1]; + combinators.includes(arr[0].charAt(0)) ? arr.unshift(arSelf) : arr.unshift(arSelf, " "); + } rule.sel = selRule .reduce((acc, curr) => { acc.push(curr.join("")); @@ -24035,145 +24376,9 @@ .reduce((acc, curr) => acc + (curr == "&" ? replace : curr), ""); } - // from https://github.com/Rich-Harris/vlq/tree/master - // credit: Rich Harris - const integer_to_char = {}; - let i = 0; - for (const char of 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=') { - integer_to_char[i++] = char; - } - function encode(value) { - if (typeof value === 'number') { - return encode_integer(value); - } - let result = ''; - for (let i = 0; i < value.length; i += 1) { - result += encode_integer(value[i]); - } - return result; - } - function encode_integer(num) { - let result = ''; - if (num < 0) { - num = (-num << 1) | 1; - } - else { - num <<= 1; - } - do { - let clamped = num & 31; - num >>>= 5; - if (num > 0) { - clamped |= 32; - } - result += integer_to_char[clamped]; - } while (num > 0); - return result; - } - /** - * Source map class - * @internal + * match url */ - class SourceMap { - /** - * Last location - */ - lastLocation = null; - /** - * Version - * @private - */ - version = 3; - /** - * Sources map - * @private - */ - sourcesMap = []; - /** - * Sources - * @private - */ - sources = []; - /** - * Map - * @private - */ - map = new Map(); - /** - * Line - * @private - */ - line = -1; - /** - * Add a location - * @param source - * @param original - */ - add(newLine, newColumn, srcId, ln, col, sourceFileName, sourceContent) { - if (!this.sourcesMap.includes(srcId)) { - if (sourceFileName == null && sourceContent != null) { - sourceFileName = "data:text/css;charset=utf-8;base64," + btoa(sourceContent); - } - this.sourcesMap.push(srcId); - this.sources.push(sourceFileName || null); - } - const line = newLine - 1; - let record; - if (line > this.line) { - this.line = line; - } - if (!this.map.has(line)) { - record = [Math.max(0, newColumn - 1), this.sourcesMap.indexOf(srcId), ln - 1, col - 1]; - this.map.set(line, [record]); - } - else { - const arr = this.map.get(line); - record = [ - Math.max(0, newColumn - 1 - arr[0][0]), - this.sourcesMap.indexOf(srcId) - arr[0][1], - ln - 1, - col - 1, - ]; - arr.push(record); - } - if (this.lastLocation != null) { - record[2] -= this.lastLocation.ln - 1; - record[3] -= this.lastLocation.col - 1; - } - this.lastLocation ??= { ln, col }; - this.lastLocation.ln = ln; - this.lastLocation.col = col; - } - /** - * Convert to URL encoded string - */ - toUrl() { - // /*# sourceMappingURL = ${url} */ - return `data:application/json;charset=utf-8;base64,${btoa(JSON.stringify(this.toJSON()))}`; - } - /** - * Convert to JSON object - */ - toJSON() { - const mappings = []; - let i = 0; - for (; i <= this.line; i++) { - if (!this.map.has(i)) { - mappings.push(""); - } - else { - mappings.push(this.map.get(i).reduce((acc, curr) => acc + (acc === "" ? "" : ",") + encode(curr), "")); - } - } - return { - version: this.version, - sources: this.sources.slice(), - mappings: mappings.join(";"), - }; - } - } - const matchUrl = /^(https?:)?\/\//; /** * return the directory name of a path @@ -24185,6 +24390,9 @@ if (path === "") { return ""; } + if (path.startsWith("data:")) { + return path; + } let i = 0; let parts = [""]; for (; i < path.length; i++) { @@ -24208,10 +24416,7 @@ if (result.length == 0) { return { parts: [], i: 0 }; } - // if (result === "/") { - // return { parts: ["/"], i: 0 }; - // } - const parts = [""]; + const parts = result == "/" ? [] : [""]; let i = 0; for (; i < result.length; i++) { const chr = result.charAt(i); @@ -24220,7 +24425,7 @@ } // else if (chr == "?" || chr == "#") { // break; - // } + // } else { parts[parts.length - 1] += chr; } @@ -24238,6 +24443,8 @@ } /** * Nomalize path + * @param path + * @private */ const normalize = memoize(function (path) { let parts = []; @@ -24266,14 +24473,20 @@ while (++k < parts.length) { // if (parts[k] == ".") { // parts.splice(k--, 1); - // } else - if (parts[k] == "..") { + // } else + if (k > 0 && parts[k] == "..") { parts.splice(k - 1, 2); k -= 2; } } return (path.charAt(0) == "/" ? "/" : "") + parts.join("/"); }); + /** + * diff path + * @param path1 + * @param path2 + * @private + */ const diff = memoize(function (path1, path2) { let { parts } = splitPath(path1); const { parts: dirs } = splitPath(path2); @@ -24305,31 +24518,52 @@ cwd ??= ""; currentDirectory ??= ""; url = normalize(url); + if (cwd !== "") { + cwd = normalize(cwd); + } if (currentDirectory !== "") { currentDirectory = normalize(currentDirectory); - if (url.startsWith(currentDirectory + "/")) { - return { - absolute: url, - relative: url.slice(currentDirectory.length + 1), - }; - } - } - if ((currentDirectory === "" || currentDirectory === ".") && cwd !== "") { - cwd = normalize(cwd); - if (url.startsWith(cwd == "/" ? cwd : cwd + "/")) { - const absolute = url; - const prefix = cwd == "/" ? cwd : cwd + "/"; - return { - absolute, - relative: absolute.startsWith(prefix) ? absolute.slice(prefix.length) : diff(absolute, cwd), - }; - } } + const dir = cwd || currentDirectory; + const absolute = dir == "" || url.startsWith("/") ? resolvePath(url) : resolvePath(dir, url); return { - absolute: url, - relative: url === "" ? "" : diff(url, cwd || currentDirectory), + absolute, + relative: dir === "" ? absolute : diff(absolute, dir), }; }); + /** + * + * @param parts + * @returns + * @private + */ + function resolvePath(...parts) { + const path = parts.filter(Boolean).join("/"); + const isAbsolute = /^[\\/]/.test(path); + const segments = path.split(/[\\/]+/); + const resolved = []; + for (const segment of segments) { + if (!segment || segment === ".") { + continue; + } + if (segment === "..") { + if (resolved.length && resolved[resolved.length - 1] !== "..") { + resolved.pop(); + } + else if (!isAbsolute) { + resolved.push(".."); + } + } + else { + resolved.push(segment); + } + } + let result = resolved.join("/"); + if (isAbsolute) { + result = "/" + result; + } + return result || (isAbsolute ? "/" : "."); + } /** * render ast @@ -24382,22 +24616,28 @@ const startTime = performance.now(); const errors = []; const sourcemap = options.sourcemap ? new SourceMap() : null; + const sourcemaps = options.sourcemap ? [] : null; const cache = Object.create(null); const sourceLocation = { - srcId: 0, - sta: 0, end: 0, }; - const linesMap = new LineMap([]); + const linesMap = options.sourcemap ? new LineMap() : null; let code = ""; if (mapping != null) { if (mapping.importMapping != null) { - for (const [key, value] of Object.entries(mapping.importMapping)) { + const absolutePath = options.resolve(options.output != null ? dirname(options.output) : dirname(options.src), options.cwd).absolute; + for (let [key, value] of Object.entries(mapping.importMapping)) { + key = options.resolve(options.resolve(key, options.cwd).absolute, absolutePath).relative; + if (!key.startsWith("/") && !key.startsWith(".")) { + key = "./" + key; + } code += `:import("${key}")${options.indent}{${options.newLine}${Object.entries(value).reduce((acc, [k, v]) => acc + (acc.length > 0 ? options.newLine : "") + `${options.indent}${v}:${options.indent}${k};`, "")}${options.newLine}}${options.newLine}`; } } code += `:export${options.indent}{${options.newLine}${Object.entries(mapping.mapping).reduce((acc, [k, v]) => acc + (acc.length > 0 ? options.newLine : "") + `${options.indent}${k}:${options.indent}${v};`, "")}${options.newLine}}${options.newLine}`; - move(sourceLocation, linesMap, code); + if (sourcemap != null) { + move(sourceLocation, linesMap, code); + } } if (options.output != null) { // @ts-ignore @@ -24409,7 +24649,7 @@ [exports.EnumToken.StyleSheetNodeType, exports.EnumToken.AtRuleNodeType, exports.EnumToken.RuleNodeType].includes(data.typ) && "chi" in data ? expand(data) - : data, options, sourcemap, sourceLocation, linesMap, errors, function reducer(acc, curr) { + : data, options, sourcemaps, sourceLocation, linesMap, errors, function reducer(acc, curr) { if (curr.typ == exports.EnumToken.CommentTokenType && options.removeComments) { if (!options.preserveLicense || !curr.val.startsWith("/*!")) { return acc; @@ -24424,6 +24664,7 @@ }, }; if (sourcemap != null) { + sourcemap.addAll(sourcemaps); result.map = sourcemap; if (options.sourcemap === "inline") { result.code += `\n/*# sourceMappingURL=${result.map.toUrl()} */`; @@ -24436,37 +24677,88 @@ * @param node * @param options * @param cache - * @param sourcemap - * @param position + * @param sourcemaps + * @param sourceLocation + * @param linesMap * @param str * * @internal */ - function updateSourceMap(node, options, cache, sourcemap, sourceLocation, linesMap, str) { - if ([ - exports.EnumToken.RuleNodeType, - exports.EnumToken.AtRuleNodeType, - exports.EnumToken.KeyFramesRuleNodeType, - exports.EnumToken.KeyframesAtRuleNodeType, - ].includes(node.typ)) { - let srcId = node[LOC]?.srcId ?? 0; - let sourceFileName = options.sourcesMap?.get(srcId)?.getFileName?.() || null; - if (sourceFileName != null && options.output != null) { - if (cache[sourceFileName] == null) { - cache[sourceFileName] = options.resolve(sourceFileName, dirname(options.output)).relative; + function updateSourceMap(node, options, cache, sourcemaps, sourceLocation, linesMap, str) { + let offset = 0; + while (true) { + if (str.charAt(offset) == options.newLine) { + offset += options.newLine.length; + continue; + } + if (str.charAt(offset) == options.indent) { + offset += options.indent.length; + continue; + } + break; + } + if (offset > 0) { + move(sourceLocation, linesMap, str.slice(0, offset)); + } + if (node[LOC] != null && + [ + exports.EnumToken.RuleNodeType, + exports.EnumToken.AtRuleNodeType, + exports.EnumToken.KeyFramesRuleNodeType, + exports.EnumToken.KeyframesAtRuleNodeType, + ].includes(node.typ)) { + const source = options.sourcesMap.get(node[LOC].srcId); + const inputSourceMap = source.getInputSourceMap(); + const offsets = source.getOffsets(node[LOC].sta); + const [newLine, newColumn] = linesMap.getOffsets(sourceLocation.end); + let records = null; + let srcId = node[LOC].srcId; + let sourceFileName = source.getFileName() || null; + let sourceContent = source.getContent() || null; + if (inputSourceMap != null && (records = inputSourceMap.find(offsets[0], offsets[1])) != null) { + for (const record of records) { + // @ts-ignore + sourceFileName = record[0] || null; + // @ts-ignore + offsets[0] = record[1]; + // @ts-ignore + offsets[1] = record[2]; + sourceContent = record[3] || null; + if (sourceFileName != null && options.output != null && !sourceFileName.startsWith("data:")) { + if (cache[sourceFileName] == null) { + const absolute = options.resolve(dirname(options.output), options.cwd) + .absolute; + const absoluteSourcePath = options.resolve(dirname(options.src || ""), options.cwd).absolute; + // resolution is relative to the source file + const absoluteSourceFileName = options.resolve(sourceFileName, absoluteSourcePath) + .absolute; + cache[sourceFileName] = options.resolve(absoluteSourceFileName, absolute).relative; + } + sourceFileName = cache[sourceFileName]; + } + sourcemaps.push([newLine, newColumn, srcId, ...offsets, sourceFileName, sourceContent]); } - sourceFileName = cache[sourceFileName]; } - // @ts-ignore - sourcemap.add(...linesMap.getOffsets(sourceLocation.end), srcId, - // @ts-ignore - ...options.sourcesMap?.get(srcId)?.getOffsets(sourceLocation.sta), sourceFileName, options.sourcesMap?.get(srcId)?.getContent?.()); + else { + if (sourceFileName != null && options.output != null && !sourceFileName.startsWith("data:")) { + if (cache[sourceFileName] == null) { + const absolute = options.resolve(dirname(options.output), options.cwd) + .absolute; + const absoluteSourceFileName = options.resolve(sourceFileName, options.cwd) + .absolute; + cache[sourceFileName] = options.resolve(absoluteSourceFileName, absolute).relative; + } + sourceFileName = cache[sourceFileName]; + } + sourcemaps.push([newLine, newColumn, srcId, ...offsets, sourceFileName, sourceContent]); + } } - move(sourceLocation, linesMap, str); + move(sourceLocation, linesMap, offset > 0 ? str.slice(offset) : str); } /** * Update position - * @param position + * @param sourceLocation + * @param linesMap * @param str */ function move(sourceLocation, linesMap, str) { @@ -24496,8 +24788,9 @@ * render ast node * @param data * @param options - * @param sourcemap - * @param position + * @param sourcemaps + * @param sourceLocation + * @param linesMap * @param errors * @param reducer * @param cache @@ -24506,13 +24799,17 @@ * * @internal */ - function renderAstNode(data, options, sourcemap, sourceLocation, linesMap, errors, reducer, cache, level = 0, indents = []) { + function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, errors, reducer, cache, level = 0, indents = []) { if (indents.length < level + 1) { indents.push(options.indent.repeat(level)); } if (indents.length < level + 2) { indents.push(options.indent.repeat(level + 1)); } + // @ts-ignore + let children = ""; + let str = ""; + let previousStr = ""; const indent = indents[level]; const indentSub = indents[level + 1]; switch (data.typ) { @@ -24530,20 +24827,17 @@ ? data.val : ""; case exports.EnumToken.StyleSheetNodeType: - return data.chi.reduce((css, node) => { - const hasPreviousContent = css !== ""; - const str = renderAstNode(node, options, sourcemap, sourceLocation, linesMap, errors, reducer, cache, level, indents); + for (const node of data.chi) { + str = renderAstNode(node, options, sourcemaps, sourceLocation, linesMap, errors, reducer, cache, level, indents); if (str === "") { - return css; - } - if (sourcemap != null && node[LOC] != null) { - updateSourceMap(node, options, cache, sourcemap, sourceLocation, linesMap, (hasPreviousContent ? options.newLine : "") + str); + continue; } - if (!hasPreviousContent) { - return str; + if (children.length > 0) { + str = options.newLine + str; } - return `${css}${options.newLine}${str}`; - }, ""); + children += str; + } + return children; case exports.EnumToken.AtRuleNodeType: case exports.EnumToken.RuleNodeType: case exports.EnumToken.KeyFramesRuleNodeType: @@ -24551,9 +24845,15 @@ if ([exports.EnumToken.AtRuleNodeType, exports.EnumToken.KeyframesAtRuleNodeType].includes(data.typ) && !("chi" in data)) { return `${indent}@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val};`; } - // @ts-ignore - let children = data.chi.reduce((css, node) => { - let str; + const prelude = [exports.EnumToken.AtRuleNodeType, exports.EnumToken.KeyframesAtRuleNodeType].includes(data.typ) + ? `@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val}${options.indent}{` + : data.sel + `${options.indent}{`; + if (sourcemaps != null) { + updateSourceMap(data, options, cache, sourcemaps, sourceLocation, linesMap, prelude); + } + let node; + for (let i = 0; i < data.chi.length; i++) { + node = data.chi[i]; if (node.typ == exports.EnumToken.CommentNodeType) { str = options.removeComments && @@ -24576,41 +24876,45 @@ : node.val) .reduce(reducer, "") .trimEnd()};`; + if (sourcemaps != null) { + if (previousStr.length > 0) { + move(sourceLocation, linesMap, previousStr); + } + } + previousStr = str === "" ? "" : options.newLine + indentSub + str; } // else if (node.typ == EnumToken.AtRuleNodeType && !("chi" in node)) { // str = `${(node).val === "" ? "" : options.indent || " "}${(node).val};`; // } else { - str = renderAstNode(node, options, sourcemap, sourceLocation, linesMap, errors, reducer, cache, level + 1, indents); - } - if (css === "") { - return str; + if (sourcemaps != null) { + if (previousStr.length > 0) { + move(sourceLocation, linesMap, previousStr); + } + } + str = renderAstNode(node, options, sourcemaps, sourceLocation, linesMap, errors, reducer, cache, level + 1, indents); + previousStr = ""; } if (str === "") { - return css; + continue; } - return `${css}${options.newLine}${indentSub}${str}`; - }, ""); - if (options.removeEmpty && children === "") { - return ""; + str = options.newLine + indentSub + str; + children += str; + } + if (sourcemaps != null && str !== "") { + move(sourceLocation, linesMap, str.endsWith(";") ? str.slice(0, -1) : str); } if (children.endsWith(";")) { children = children.slice(0, -1); } - const rendered = [exports.EnumToken.AtRuleNodeType, exports.EnumToken.KeyframesAtRuleNodeType].includes(data.typ) - ? `@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val}${options.indent}{${options.newLine}` + - (children === "" ? "" : indentSub + children + options.newLine) + - indent + - `}` - : data.sel + - `${options.indent}{${options.newLine}` + - (children === "" ? "" : indentSub + children + options.newLine) + - indent + - `}`; - if (sourcemap != null && data[LOC] != null) { - updateSourceMap(data, options, cache, sourcemap, { ...sourceLocation }, linesMap.clone(), rendered); - } - return rendered; + if (options.removeEmpty && children === "") { + return ""; + } + const end = options.newLine + indent + `}`; + if (sourcemaps != null) { + move(sourceLocation, linesMap, end); + } + return prelude + children + end; // case EnumToken.CssVariableTokenType: // case EnumToken.CssVariableImportTokenType: // return `@value ${(data).val}:${options.indent}${filterValues( @@ -24637,6 +24941,9 @@ * render ast token * @param token * @param options + * @param cache + * @param reducer + * @param errors * @private */ function renderValue(token, options = {}, cache = Object.create(null), reducer, errors) { @@ -28674,7 +28981,7 @@ if (replacement == null) { continue; } - if (replacement == null || replacement == node) { + if (replacement == node) { continue; } // @ts-ignore @@ -28720,7 +29027,7 @@ if (replacement == null) { continue; } - if (replacement == null || replacement == node) { + if (replacement == node) { continue; } // @ts-ignore @@ -28754,7 +29061,7 @@ if (replacement == null) { continue; } - if (replacement != null && replacement != node) { + if (replacement != node) { node = replacement; } } @@ -28787,7 +29094,7 @@ if (result == null) { continue; } - if (result != null && result != node) { + if (result != node) { node = result; } if (Array.isArray(node)) { @@ -28972,10 +29279,9 @@ if (node.typ == exports.EnumToken.DeclarationNodeType) { if (node.nam.startsWith("--")) { if (!(node.nam in namesMapping)) { - let result = moduleSettings.scoped & exports.ModuleScopeEnumOptions.Global + let value = moduleSettings.scoped & exports.ModuleScopeEnumOptions.Global ? node.nam : moduleSettings.generateScopedName(node.nam, moduleSettings.filePath, moduleSettings.pattern, moduleSettings.hashLength); - let value = result; mapping[node.nam] = "--" + (moduleSettings.naming & exports.ModuleCaseTransformEnum.DashCaseOnly || @@ -29017,10 +29323,9 @@ continue; } if (!(rule.val in mapping)) { - let result = moduleSettings.scoped & exports.ModuleScopeEnumOptions.Global + let value = moduleSettings.scoped & exports.ModuleScopeEnumOptions.Global ? rule.val : moduleSettings.generateScopedName(rule.val, moduleSettings.filePath, moduleSettings.pattern, moduleSettings.hashLength); - let value = result; mapping[rule.val] = (rule.typ == exports.EnumToken.DashedIdenTokenType ? "--" : "") + (moduleSettings.naming & exports.ModuleCaseTransformEnum.DashCaseOnly || @@ -29191,10 +29496,10 @@ "unset", ].includes(value.val)) { if (!(value.val in mapping)) { - const result = moduleSettings.scoped & exports.ModuleScopeEnumOptions.Global - ? value.val - : moduleSettings.generateScopedName(value.val, moduleSettings.filePath, moduleSettings.pattern, moduleSettings.hashLength); - mapping[value.val] = result; + mapping[value.val] = + moduleSettings.scoped & exports.ModuleScopeEnumOptions.Global + ? value.val + : moduleSettings.generateScopedName(value.val, moduleSettings.filePath, moduleSettings.pattern, moduleSettings.hashLength); revMapping[mapping[value.val]] = value.val; } value.val = mapping[value.val]; @@ -29266,10 +29571,9 @@ if (value.typ == exports.EnumToken.ClassSelectorTokenType) { const val = value.val.slice(1); if (!(val in mapping)) { - const result = moduleSettings.scoped & exports.ModuleScopeEnumOptions.Global + let value = moduleSettings.scoped & exports.ModuleScopeEnumOptions.Global ? val : moduleSettings.generateScopedName(val, moduleSettings.filePath, moduleSettings.pattern, moduleSettings.hashLength); - let value = result; mapping[val] = moduleSettings.naming & exports.ModuleCaseTransformEnum.DashCaseOnly || moduleSettings.naming & exports.ModuleCaseTransformEnum.CamelCaseOnly @@ -29302,10 +29606,9 @@ if ((prefix == "--" && value.typ == exports.EnumToken.DashedIdenTokenType) || (prefix == "" && value.typ == exports.EnumToken.IdenTokenType)) { if (!(value.val in mapping)) { - const result = moduleSettings.scoped & exports.ModuleScopeEnumOptions.Global + let val = moduleSettings.scoped & exports.ModuleScopeEnumOptions.Global ? value.val : moduleSettings.generateScopedName(value.val, moduleSettings.filePath, moduleSettings.pattern, moduleSettings.hashLength); - let val = result; mapping[value.val] = prefix + (moduleSettings.naming & exports.ModuleCaseTransformEnum.DashCaseOnly || @@ -29655,7 +29958,7 @@ const token = node[TOKENS][0]; const url = token.typ == exports.EnumToken.StringTokenType ? token.val.slice(1, -1) : token.val; try { - const src = options.resolve(url, options.src || options.cwd); + const src = options.resolve(url, options.src ? dirname(options.src) : options.cwd); const result = options.load(src); const stream = result instanceof Promise || Object.getPrototypeOf(result).constructor.name == "AsyncFunction" ? await result @@ -30129,6 +30432,7 @@ parentRule.chi.splice(parentRule.chi.indexOf(node), 1); continue; } + const resolvedSrc = options.resolve(options.src, options.cwd); for (const token of composeSelectors) { // composes: a b c; if (token.r == null) { @@ -30198,8 +30502,10 @@ setParent: false, src: src.relative, })); - const srcIndex = (src.relative.startsWith("/") || src.relative.startsWith("../") ? "" : "./") + - src.relative; + let srcIndex = options.resolve(src.absolute, resolvedSrc.absolute).relative; + if (!srcIndex.startsWith("/") && !srcIndex.startsWith("../")) { + srcIndex = `./${srcIndex}`; + } if (Object.keys(root.mapping).length > 0) { importMapping[srcIndex] = {}; } @@ -30451,26 +30757,6 @@ exports.EnumToken.DescendantCombinatorTokenType) { parent[TOKENS].splice(index, 1); } - // if (val == ":global") { - // for (; index < (parent as AstRule)[TOKENS]!.length; index++) { - // if ( - // (parent as AstRule)[TOKENS]![index].typ == - // EnumToken.CommaTokenType || - // ([ - // EnumToken.PseudoClassFuncTokenType, - // EnumToken.PseudoClassTokenType, - // ].includes((parent as AstRule)[TOKENS]![index].typ) && - // [":global", ":local"].includes( - // ( - // (parent as AstRule)[TOKENS]![index] as PseudoClassToken - // ).val.toLowerCase(), - // )) - // ) { - // break; - // } - // global.add((parent as AstRule)[TOKENS]![index]); - // } - // } } break; } @@ -30484,12 +30770,6 @@ case ":local": parent[TOKENS].splice(parent[TOKENS].indexOf(value), 1, ...value.chi); break; - // (parent as AstRule)[TOKENS]!.splice( - // (parent as AstRule)[TOKENS]!.indexOf(value), - // 1, - // ...(value as FunctionToken).chi, - // ); - // break; } } })) { @@ -30775,6 +31055,8 @@ return null; } /** + * @param stream + * @param context * @param options * @param errors * @param parseAsBlock @@ -30851,7 +31133,6 @@ parseAsBlock = blockAllowed; } if (syntax != null && atRule.nam !== "layer" && parseAsBlock !== blockAllowed) { - success = false; errors.push({ action: "drop", node: atRule, @@ -31339,7 +31620,7 @@ action: "drop", node: atRule, location: options.source.getSourceLocation(atRule[LOC].sta), - message: "node is allowd only in @page rule", + message: "node is allowed only in @page rule", }); } else { @@ -31478,9 +31759,6 @@ if (result.errors.length > 0) { errors.push(...result.errors); } - // else if (atRuleName === "document") { - // parseUrlToken(stream); - // } if (result.success) { let i = 0; const stack = []; @@ -31824,6 +32102,52 @@ ResponseType[ResponseType["ArrayBuffer"] = 2] = "ArrayBuffer"; })(exports.ResponseType || (exports.ResponseType = {})); + /** + * parse result. process input sourcemap + * @param result + * @param options + * @returns + * @private + */ + function parseResult(result, options) { + if (options.sourcemap != null && options.source.getInputSourceMap() == null) { + if (options.inputSourceMap != null) { + options.source.setInputSourceMap(options.inputSourceMap); + } + else { + // extract inline source map from the input CSS + const token = result.ast.chi.at(-1); + if (token?.typ == exports.EnumToken.CommentTokenType && + token.val.startsWith("/*# sourceMappingURL=")) { + const data = token.val.slice(21, -2).trim(); + let sourcemap; + let encoding = ""; + if (data.startsWith("data:")) { + let offset = data.indexOf(",") + 1; + if (offset == 0) { + offset = data.lastIndexOf(";") + 1; + } + else { + encoding = data.slice(data.lastIndexOf(";") + 1, offset - 1); + } + if (encoding == "base64") { + sourcemap = atob(data.slice(offset)); + } + else { + sourcemap = decodeURIComponent(data.slice(offset)); + } + options.source.setInputSourceMap(sourcemap); + } + } + } + } + if (options.module) { + const { revMapping, ...res } = result; + return res; + } + return result; + } + /** * Load file or url * @param url @@ -31960,7 +32284,7 @@ } options ??= {}; options.src ??= ""; - options.sourcesMap ??= new Map; + options.sourcesMap ??= new Map(); Object.assign(options, { resolve, dirname, @@ -31985,13 +32309,10 @@ currentPosition: -1, }; const result = doParseSync(tokenize(options.parseInfo), options); - const { revMapping, ...res } = result; - return res; + return !options.module && !options.inputSourceMap ? result : parseResult(result, options); } /** * Transform css - * @param css - * @param options * * ```ts * @@ -32002,6 +32323,7 @@ * console.log(result.code); * ``` * + * @param args */ function transformSync(...args) { let options; @@ -32051,8 +32373,6 @@ } /** * Parse css - * @param stream - * @param options * * Example: * @@ -32076,6 +32396,7 @@ * * console.log(result.ast); * ``` + * @param args */ async function parse(...args) { let options; @@ -32097,7 +32418,7 @@ } options ??= {}; options.src ??= ""; - options.sourcesMap ??= new Map; + options.sourcesMap ??= new Map(); Object.assign(options, { load, resolve, @@ -32121,10 +32442,7 @@ position: 0, currentPosition: -1, }; - return doParse(stream instanceof ReadableStream ? tokenizeStream(stream, options.parseInfo) : tokenize(options.parseInfo), options).then((result) => { - const { revMapping, ...res } = result; - return res; - }); + return doParse(stream instanceof ReadableStream ? tokenizeStream(stream, options.parseInfo) : tokenize(options.parseInfo), options).then((result) => (!options.module && !options.inputSourceMap ? result : parseResult(result, options))); } /** * Transform css file @@ -32158,8 +32476,6 @@ } /** * Transform css - * @param css - * @param options * * Example: * @@ -32177,6 +32493,7 @@ * * console.log(result.code); * ``` + * @param args */ async function transform(...args) { let options; diff --git a/dist/index.cjs b/dist/index.cjs index 2f51323c..fb531363 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -12669,7 +12669,7 @@ function matchSyntax(syntaxes, context, options) { if (syntaxes[i].isList) { result = matchListSyntax(syntaxes[i], context.slice(), options); if (result.success) { - options.visited.get(token).delete(syntaxes[i]); + options.visited.get(token)?.delete?.(syntaxes[i]); if (result.context.done()) { context.end(); return { @@ -21053,7 +21053,7 @@ class TransformCssFeature { } /** - * Search the ast sub-tree and return the first match + * Search the ast subtree and return the first match * * ```ts * // find the first ast declaration node which name is 'aspect-ratio' @@ -21392,6 +21392,315 @@ var allFeatures = /*#__PURE__*/Object.freeze({ TransformCssFeature: TransformCssFeature }); +// from https://github.com/Rich-Harris/vlq/tree/master +// credit: Rich Harris +const integer_to_char = {}; +const char_to_integer = {}; +let i = 0; +for (const char of 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=') { + char_to_integer[char] = i; + integer_to_char[i++] = char; +} +/** + * @param {string} str + */ +function decode(str) { + /** @type {number[]} */ + let result = []; + let shift = 0; + let value = 0; + for (let i = 0; i < str.length; i += 1) { + let integer = char_to_integer[str[i]]; + if (integer === undefined) { + throw new Error('Invalid character (' + str[i] + ')'); + } + const has_continuation_bit = integer & 32; + integer &= 31; + value += integer << shift; + if (has_continuation_bit) { + shift += 5; + } + else { + const should_negate = value & 1; + value >>>= 1; + if (should_negate) { + result.push(value === 0 ? -2147483648 : -value); + } + else { + result.push(value); + } + // reset + value = shift = 0; + } + } + return result; +} +/** + * + * @param value + * @returns + */ +function encode(value) { + if (typeof value === 'number') { + return encode_integer(value); + } + let result = ''; + for (let i = 0; i < value.length; i += 1) { + result += encode_integer(value[i]); + } + return result; +} +function encode_integer(num) { + let result = ''; + if (num < 0) { + num = (-num << 1) | 1; + } + else { + num <<= 1; + } + do { + let clamped = num & 31; + num >>>= 5; + if (num > 0) { + clamped |= 32; + } + result += integer_to_char[clamped]; + } while (num > 0); + return result; +} + +/** + * Generate and parse source map + */ +class SourceMap { + /** + * + * @private + */ + keys = new Set(); + /** + * Last location + */ + lastLocation = null; + /** + * Version + * @private + */ + version = 3; + /** + * Sources map + * @private + */ + sourcesMap = []; + /** + * Sources content + * @private + */ + sourcesContent = []; + /** + * Sources + * @private + */ + sources = []; + /** + * Map + * @private + * + */ + map = new Map(); + /** + * Map + * @private + * + */ + reverseMap = new Map(); + /** + * Line + * @private + */ + line = -1; + /** + * + * @param sourcemaps + */ + constructor(sourcemaps) { + if (typeof sourcemaps === "string") { + sourcemaps = JSON.parse(sourcemaps); + } + if (sourcemaps != null) { + this.sources = sourcemaps.sources?.slice() ?? []; + this.sourcesContent = sourcemaps.sourcesContent?.slice() ?? []; + const decodedMappings = sourcemaps.mappings + .split(";") + .map((mapping) => mapping.split(",").map((mapping) => decode(mapping))); + this.line = decodedMappings.length - 1; + for (let index = 0; index < decodedMappings.length; index++) { + if (decodedMappings[index].length == 0 || + (decodedMappings[index].length == 1 && decodedMappings[index][0].length == 0)) { + continue; + } + this.map.set(index, decodedMappings[index]); + } + this.computePositions(); + } + } + /** + * Add all location + * @param maps + */ + addAll(maps) { + for (let [newLine, newColumn, srcId, ln, col, sourceFileName, sourceContent] of maps) { + const key = `${srcId}:${ln}:${sourceFileName}:${col}:${newLine}:${newColumn}:${sourceContent}`; + const sourcemap = `${srcId}:${sourceFileName}:${sourceContent}`; + if (this.keys.has(key)) { + continue; + } + this.keys.add(key); + if (!this.sourcesMap.includes(sourcemap)) { + this.sourcesMap.push(sourcemap); + this.sources.push(sourceFileName || null); + this.sourcesContent.push((sourceFileName != null ? null : sourceContent) || null); + } + const line = newLine - 1; + let record; + if (line > this.line) { + this.line = line; + } + if (!this.map.has(line)) { + record = [Math.max(0, newColumn - 1), this.sourcesMap.indexOf(sourcemap), ln - 1, col - 1]; + this.map.set(line, [record]); + } + else { + const arr = this.map.get(line); + record = [ + Math.max(0, newColumn - 1) - arr[0][0], + this.sourcesMap.indexOf(sourcemap) - arr[0][1], + ln - 1, + col - 1, + ]; + arr.push(record); + } + if (this.lastLocation != null) { + record[2] -= this.lastLocation.ln - 1; + record[3] -= this.lastLocation.col - 1; + } + this.lastLocation ??= { ln, col }; + this.lastLocation.ln = ln; + this.lastLocation.col = col; + } + } + /** + * compute original positions + */ + computePositions() { + this.reverseMap.clear(); + let sourceFileIndex = 0; // second field + let sourceCodeLine = 0; // third field + let sourceCodeColumn = 0; // fourth field + let nameIndex = 0; // fifth field + let generatedCodeColumn; + let result; + // mappings to original source + for (let [i, line] of this.map.entries()) { + if (line.length === 0 || (line.length === 1 && line[0].length === 0)) { + continue; + } + generatedCodeColumn = line[0][0]; // first field - reset each time + line = line + .map((segment, index, array) => { + if (segment.length === 0) { + return []; + } + generatedCodeColumn = index == 0 ? segment[0] : segment[0] + array[0][0]; + result = [generatedCodeColumn]; + if (segment.length <= 1) { + return result; + } + sourceFileIndex = index == 0 ? segment[1] : segment[1] + array[0][1]; + sourceCodeLine += segment[2]; + sourceCodeColumn += segment[3]; + result.push(sourceFileIndex, sourceCodeLine, sourceCodeColumn); + if (segment.length === 5) { + nameIndex += segment[4]; + result.push(nameIndex); + } + return result; + }) + .sort((a, b) => { + if (a[1] !== b[1]) { + return a[1] - b[1]; + } + return a[0] - b[0]; + }); + if (line.length == 0 || (line.length == 1 && line[0].length == 0)) { + continue; + } + this.reverseMap.set(i, line); + } + } + /** + * retrieve original sources, lines and columns + * @param line generated line + * @param column generated column + */ + find(line, column) { + if (!this.reverseMap.has(--line)) { + return null; + } + column--; + const result = []; + for (const record of this.reverseMap.get(line)) { + if (record.length == 0 || record[0] < column) { + continue; + } + if (record[0] > column) { + break; + } + result.push([ + this.sources?.[record[1]] ?? null, + record[2] + 1, + record[3] + 1, + this.sourcesContent?.[record[1]] ?? null, + ]); + } + return result.length == 0 ? null : result; + } + /** + * Convert to URL encoded string + */ + toUrl() { + // /*# sourceMappingURL = ${url} */ + return `data:application/json;charset=utf-8;base64,${btoa(JSON.stringify(this.toJSON()))}`; + } + /** + * Convert to JSON object + */ + toJSON() { + const mappings = []; + let i = 0; + for (; i <= this.line; i++) { + if (!this.map.has(i)) { + mappings.push(""); + } + else { + mappings.push(this.map.get(i).reduce((acc, curr) => acc + (acc === "" ? "" : ",") + encode(curr), "")); + } + } + return { + version: this.version, + sources: this.sources.slice(), + sourcesContent: this.sourcesContent?.slice(), + mappings: mappings.join(";"), + }; + } + /** + * to string + */ + toString() { + return JSON.stringify(this); + } +} + /** * Compute line and column of the offset */ @@ -21404,7 +21713,7 @@ class LineMap { * Constructor * @param lines */ - constructor(lines) { + constructor(lines = []) { if (lines.length === 0) { lines.push(0); } @@ -21422,7 +21731,7 @@ class LineMap { } const column = offset - this.lineStarts[line]; // [line, column] - return [line + 1, column === 0 ? 1 : column]; + return [line + 1, line === 0 ? column + 1 : column]; } /** * search the greatest index of the value less than or equal to offset @@ -21477,6 +21786,7 @@ let sourceId = 0; * Source file helper class */ class SourceFile { + inputSourceMap = null; /** * Source file ID */ @@ -21495,7 +21805,6 @@ class SourceFile { content; /** * Constructor - * @param id * @param content * @param lines * @param file @@ -21509,7 +21818,6 @@ class SourceFile { /** * Update source content * @param content - * @param lines */ append(content) { this.content += content; @@ -21567,6 +21875,20 @@ class SourceFile { addLineStart(lineStart) { this.lineStarts.addLineStart(lineStart); } + /** + * set input source map + * @param inputSourceMap + */ + setInputSourceMap(inputSourceMap) { + this.inputSourceMap = inputSourceMap == null ? null : new SourceMap(inputSourceMap); + } + /** + * return input source map + * @returns + */ + getInputSourceMap() { + return this.inputSourceMap; + } } const SymbolsMapTokens = { @@ -21943,7 +22265,7 @@ function next(parseInfo, count = 1) { return char; } /** - * Tokenize css string + * Tokenize CSS string * @param parseInfo * @param yieldEOFToken */ @@ -21970,8 +22292,6 @@ function tokenize(parseInfo, yieldEOFToken = true) { parseInfo.buffer = ""; while ((value = peek(parseInfo))) { charCode = value.charCodeAt(0); - // nextCharCode = nextValue.charCodeAt(0); - // console.debug({value, buffer}); switch (charCode) { case 61 /* TokenMap.EQUALS */: if (buffer.length > 0) { @@ -22336,10 +22656,6 @@ function tokenize(parseInfo, yieldEOFToken = true) { break; } buffer += value + next(parseInfo); - // buffer += - // (parseInfo.offset == parseInfo.currentPosition - // ? parseInfo.buffer.slice(-1) - // : parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset - 1)) + value; break; case 39 /* TokenMap.SINGLE_QUOTE */: case 34 /* TokenMap.DOUBLE_QUOTE */: @@ -22426,13 +22742,16 @@ const features = Object.values(allFeatures).sort((a, b) => a.ordering - b.orderi * @param errors * @param nestingContent * + * @param context * @private */ -function minify(ast, options = {}, recursive = false, errors, nestingContent, context = {}) { +function minify(ast, opt = {}, recursive = false, errors, nestingContent, context = {}) { let preprocess = false; let postprocess = false; let parents; let replacement; + // @ts-ignore + let { sourcemap, module, ...options } = opt; if (!("features" in options)) { // @ts-ignore options = { @@ -22629,9 +22948,9 @@ function transformAtRuleMediaPrelude(values) { * Minify at-rule media * - remove redundant tokens * - generate range queries - * @param ast * * @private + * @param tokens */ function minifyAtRuleMedia(tokens) { let hasUpdates = false; @@ -22729,7 +23048,6 @@ function doMinify(ast, options = {}, recursive = false, errors, nestingContent, } while (previous?.typ === exports.EnumToken.CommentNodeType) { previous = ast.chi[--nodeIndex]; - continue; } node = ast.chi[i]; if (node.typ === exports.EnumToken.AtRuleNodeType && node.nam === "font-face") { @@ -23200,7 +23518,9 @@ function optimizeSelector(selector) { } break; } - selector.forEach((selector) => selector.splice(0, optimized.length)); + for (let i1 = 0; i1 < selector.length; i1++) { + selector[i1].splice(0, optimized.length); + } let reducible = optimized.length == 1; if (optimized[0] == "&") { if (optimized[1] == " ") { @@ -23582,7 +23902,6 @@ function wrapNodes(previous, node, match, ast, reducer, i, nodeIndex) { * Diff nodes * @param n1 * @param n2 - * @param reducer * @param options * * @private @@ -23701,17 +24020,36 @@ function diff$1(n1, n2, options = {}) { // @ts-ignore chi: intersect.reverse(), }; + let op = { level: 0, ...options }; if (result == null || [n1, n2].reduce((acc, curr) => { let css = options.cache.get(curr); if (css == null) { - css = doRender(curr, options).code; + let level = 0; + let parent = curr[PARENT]; + while (parent != null && parent.typ != exports.EnumToken.StyleSheetNodeType) { + level++; + parent = parent[PARENT]; + } + op.level = level; + css = doRender(curr, op).code; options.cache.set(curr, css); } return curr.chi.length == 0 ? acc : acc + css.length; }, 0) <= [node1, node2, result].reduce((acc, curr) => { - const css = doRender(curr, options).code; + let css = options.cache.get(curr); + if (css != null) { + return curr.chi.length == 0 ? acc : acc + css.length; + } + let level = 0; + let parent = curr[PARENT]; + while (parent != null && parent.typ != exports.EnumToken.StyleSheetNodeType) { + level++; + parent = parent[PARENT]; + } + op.level = level; + css = doRender(curr, op).code; return curr.chi.length == 0 ? acc : acc + css.length; }, 0)) { if (node1.chi.length != 0 && node2.chi.length != 0) { @@ -23846,7 +24184,10 @@ function expandRule(node) { ast.chi.splice(i--, 1); continue; } - selRule.forEach((arr) => combinators.includes(arr[0].charAt(0)) ? arr.unshift(arSelf) : arr.unshift(arSelf, " ")); + for (let i1 = 0; i1 < selRule.length; i1++) { + const arr = selRule[i1]; + combinators.includes(arr[0].charAt(0)) ? arr.unshift(arSelf) : arr.unshift(arSelf, " "); + } rule.sel = selRule .reduce((acc, curr) => { acc.push(curr.join("")); @@ -24038,145 +24379,9 @@ function replaceCompoundLiteral(selector, replace) { .reduce((acc, curr) => acc + (curr == "&" ? replace : curr), ""); } -// from https://github.com/Rich-Harris/vlq/tree/master -// credit: Rich Harris -const integer_to_char = {}; -let i = 0; -for (const char of 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=') { - integer_to_char[i++] = char; -} -function encode(value) { - if (typeof value === 'number') { - return encode_integer(value); - } - let result = ''; - for (let i = 0; i < value.length; i += 1) { - result += encode_integer(value[i]); - } - return result; -} -function encode_integer(num) { - let result = ''; - if (num < 0) { - num = (-num << 1) | 1; - } - else { - num <<= 1; - } - do { - let clamped = num & 31; - num >>>= 5; - if (num > 0) { - clamped |= 32; - } - result += integer_to_char[clamped]; - } while (num > 0); - return result; -} - /** - * Source map class - * @internal + * match url */ -class SourceMap { - /** - * Last location - */ - lastLocation = null; - /** - * Version - * @private - */ - version = 3; - /** - * Sources map - * @private - */ - sourcesMap = []; - /** - * Sources - * @private - */ - sources = []; - /** - * Map - * @private - */ - map = new Map(); - /** - * Line - * @private - */ - line = -1; - /** - * Add a location - * @param source - * @param original - */ - add(newLine, newColumn, srcId, ln, col, sourceFileName, sourceContent) { - if (!this.sourcesMap.includes(srcId)) { - if (sourceFileName == null && sourceContent != null) { - sourceFileName = "data:text/css;charset=utf-8;base64," + btoa(sourceContent); - } - this.sourcesMap.push(srcId); - this.sources.push(sourceFileName || null); - } - const line = newLine - 1; - let record; - if (line > this.line) { - this.line = line; - } - if (!this.map.has(line)) { - record = [Math.max(0, newColumn - 1), this.sourcesMap.indexOf(srcId), ln - 1, col - 1]; - this.map.set(line, [record]); - } - else { - const arr = this.map.get(line); - record = [ - Math.max(0, newColumn - 1 - arr[0][0]), - this.sourcesMap.indexOf(srcId) - arr[0][1], - ln - 1, - col - 1, - ]; - arr.push(record); - } - if (this.lastLocation != null) { - record[2] -= this.lastLocation.ln - 1; - record[3] -= this.lastLocation.col - 1; - } - this.lastLocation ??= { ln, col }; - this.lastLocation.ln = ln; - this.lastLocation.col = col; - } - /** - * Convert to URL encoded string - */ - toUrl() { - // /*# sourceMappingURL = ${url} */ - return `data:application/json;charset=utf-8;base64,${btoa(JSON.stringify(this.toJSON()))}`; - } - /** - * Convert to JSON object - */ - toJSON() { - const mappings = []; - let i = 0; - for (; i <= this.line; i++) { - if (!this.map.has(i)) { - mappings.push(""); - } - else { - mappings.push(this.map.get(i).reduce((acc, curr) => acc + (acc === "" ? "" : ",") + encode(curr), "")); - } - } - return { - version: this.version, - sources: this.sources.slice(), - mappings: mappings.join(";"), - }; - } -} - const matchUrl = /^(https?:)?\/\//; /** * return the directory name of a path @@ -24188,6 +24393,9 @@ function dirname(path) { if (path === "") { return ""; } + if (path.startsWith("data:")) { + return path; + } let i = 0; let parts = [""]; for (; i < path.length; i++) { @@ -24211,10 +24419,7 @@ function splitPath(result) { if (result.length == 0) { return { parts: [], i: 0 }; } - // if (result === "/") { - // return { parts: ["/"], i: 0 }; - // } - const parts = [""]; + const parts = result == "/" ? [] : [""]; let i = 0; for (; i < result.length; i++) { const chr = result.charAt(i); @@ -24223,7 +24428,7 @@ function splitPath(result) { } // else if (chr == "?" || chr == "#") { // break; - // } + // } else { parts[parts.length - 1] += chr; } @@ -24241,6 +24446,8 @@ function splitPath(result) { } /** * Nomalize path + * @param path + * @private */ const normalize = memoize(function (path) { let parts = []; @@ -24269,14 +24476,20 @@ const normalize = memoize(function (path) { while (++k < parts.length) { // if (parts[k] == ".") { // parts.splice(k--, 1); - // } else - if (parts[k] == "..") { + // } else + if (k > 0 && parts[k] == "..") { parts.splice(k - 1, 2); k -= 2; } } return (path.charAt(0) == "/" ? "/" : "") + parts.join("/"); }); +/** + * diff path + * @param path1 + * @param path2 + * @private + */ const diff = memoize(function (path1, path2) { let { parts } = splitPath(path1); const { parts: dirs } = splitPath(path2); @@ -24308,31 +24521,52 @@ const resolve = memoize(function (url, currentDirectory, cwd) { cwd ??= ""; currentDirectory ??= ""; url = normalize(url); + if (cwd !== "") { + cwd = normalize(cwd); + } if (currentDirectory !== "") { currentDirectory = normalize(currentDirectory); - if (url.startsWith(currentDirectory + "/")) { - return { - absolute: url, - relative: url.slice(currentDirectory.length + 1), - }; - } - } - if ((currentDirectory === "" || currentDirectory === ".") && cwd !== "") { - cwd = normalize(cwd); - if (url.startsWith(cwd == "/" ? cwd : cwd + "/")) { - const absolute = url; - const prefix = cwd == "/" ? cwd : cwd + "/"; - return { - absolute, - relative: absolute.startsWith(prefix) ? absolute.slice(prefix.length) : diff(absolute, cwd), - }; - } } + const dir = cwd || currentDirectory; + const absolute = dir == "" || url.startsWith("/") ? resolvePath(url) : resolvePath(dir, url); return { - absolute: url, - relative: url === "" ? "" : diff(url, cwd || currentDirectory), + absolute, + relative: dir === "" ? absolute : diff(absolute, dir), }; }); +/** + * + * @param parts + * @returns + * @private + */ +function resolvePath(...parts) { + const path = parts.filter(Boolean).join("/"); + const isAbsolute = /^[\\/]/.test(path); + const segments = path.split(/[\\/]+/); + const resolved = []; + for (const segment of segments) { + if (!segment || segment === ".") { + continue; + } + if (segment === "..") { + if (resolved.length && resolved[resolved.length - 1] !== "..") { + resolved.pop(); + } + else if (!isAbsolute) { + resolved.push(".."); + } + } + else { + resolved.push(segment); + } + } + let result = resolved.join("/"); + if (isAbsolute) { + result = "/" + result; + } + return result || (isAbsolute ? "/" : "."); +} /** * render ast @@ -24385,22 +24619,28 @@ function doRender(data, options = {}, mapping) { const startTime = performance.now(); const errors = []; const sourcemap = options.sourcemap ? new SourceMap() : null; + const sourcemaps = options.sourcemap ? [] : null; const cache = Object.create(null); const sourceLocation = { - srcId: 0, - sta: 0, end: 0, }; - const linesMap = new LineMap([]); + const linesMap = options.sourcemap ? new LineMap() : null; let code = ""; if (mapping != null) { if (mapping.importMapping != null) { - for (const [key, value] of Object.entries(mapping.importMapping)) { + const absolutePath = options.resolve(options.output != null ? dirname(options.output) : dirname(options.src), options.cwd).absolute; + for (let [key, value] of Object.entries(mapping.importMapping)) { + key = options.resolve(options.resolve(key, options.cwd).absolute, absolutePath).relative; + if (!key.startsWith("/") && !key.startsWith(".")) { + key = "./" + key; + } code += `:import("${key}")${options.indent}{${options.newLine}${Object.entries(value).reduce((acc, [k, v]) => acc + (acc.length > 0 ? options.newLine : "") + `${options.indent}${v}:${options.indent}${k};`, "")}${options.newLine}}${options.newLine}`; } } code += `:export${options.indent}{${options.newLine}${Object.entries(mapping.mapping).reduce((acc, [k, v]) => acc + (acc.length > 0 ? options.newLine : "") + `${options.indent}${k}:${options.indent}${v};`, "")}${options.newLine}}${options.newLine}`; - move(sourceLocation, linesMap, code); + if (sourcemap != null) { + move(sourceLocation, linesMap, code); + } } if (options.output != null) { // @ts-ignore @@ -24412,7 +24652,7 @@ function doRender(data, options = {}, mapping) { [exports.EnumToken.StyleSheetNodeType, exports.EnumToken.AtRuleNodeType, exports.EnumToken.RuleNodeType].includes(data.typ) && "chi" in data ? expand(data) - : data, options, sourcemap, sourceLocation, linesMap, errors, function reducer(acc, curr) { + : data, options, sourcemaps, sourceLocation, linesMap, errors, function reducer(acc, curr) { if (curr.typ == exports.EnumToken.CommentTokenType && options.removeComments) { if (!options.preserveLicense || !curr.val.startsWith("/*!")) { return acc; @@ -24427,6 +24667,7 @@ function doRender(data, options = {}, mapping) { }, }; if (sourcemap != null) { + sourcemap.addAll(sourcemaps); result.map = sourcemap; if (options.sourcemap === "inline") { result.code += `\n/*# sourceMappingURL=${result.map.toUrl()} */`; @@ -24439,37 +24680,88 @@ function doRender(data, options = {}, mapping) { * @param node * @param options * @param cache - * @param sourcemap - * @param position + * @param sourcemaps + * @param sourceLocation + * @param linesMap * @param str * * @internal */ -function updateSourceMap(node, options, cache, sourcemap, sourceLocation, linesMap, str) { - if ([ - exports.EnumToken.RuleNodeType, - exports.EnumToken.AtRuleNodeType, - exports.EnumToken.KeyFramesRuleNodeType, - exports.EnumToken.KeyframesAtRuleNodeType, - ].includes(node.typ)) { - let srcId = node[LOC]?.srcId ?? 0; - let sourceFileName = options.sourcesMap?.get(srcId)?.getFileName?.() || null; - if (sourceFileName != null && options.output != null) { - if (cache[sourceFileName] == null) { - cache[sourceFileName] = options.resolve(sourceFileName, dirname(options.output)).relative; +function updateSourceMap(node, options, cache, sourcemaps, sourceLocation, linesMap, str) { + let offset = 0; + while (true) { + if (str.charAt(offset) == options.newLine) { + offset += options.newLine.length; + continue; + } + if (str.charAt(offset) == options.indent) { + offset += options.indent.length; + continue; + } + break; + } + if (offset > 0) { + move(sourceLocation, linesMap, str.slice(0, offset)); + } + if (node[LOC] != null && + [ + exports.EnumToken.RuleNodeType, + exports.EnumToken.AtRuleNodeType, + exports.EnumToken.KeyFramesRuleNodeType, + exports.EnumToken.KeyframesAtRuleNodeType, + ].includes(node.typ)) { + const source = options.sourcesMap.get(node[LOC].srcId); + const inputSourceMap = source.getInputSourceMap(); + const offsets = source.getOffsets(node[LOC].sta); + const [newLine, newColumn] = linesMap.getOffsets(sourceLocation.end); + let records = null; + let srcId = node[LOC].srcId; + let sourceFileName = source.getFileName() || null; + let sourceContent = source.getContent() || null; + if (inputSourceMap != null && (records = inputSourceMap.find(offsets[0], offsets[1])) != null) { + for (const record of records) { + // @ts-ignore + sourceFileName = record[0] || null; + // @ts-ignore + offsets[0] = record[1]; + // @ts-ignore + offsets[1] = record[2]; + sourceContent = record[3] || null; + if (sourceFileName != null && options.output != null && !sourceFileName.startsWith("data:")) { + if (cache[sourceFileName] == null) { + const absolute = options.resolve(dirname(options.output), options.cwd) + .absolute; + const absoluteSourcePath = options.resolve(dirname(options.src || ""), options.cwd).absolute; + // resolution is relative to the source file + const absoluteSourceFileName = options.resolve(sourceFileName, absoluteSourcePath) + .absolute; + cache[sourceFileName] = options.resolve(absoluteSourceFileName, absolute).relative; + } + sourceFileName = cache[sourceFileName]; + } + sourcemaps.push([newLine, newColumn, srcId, ...offsets, sourceFileName, sourceContent]); } - sourceFileName = cache[sourceFileName]; } - // @ts-ignore - sourcemap.add(...linesMap.getOffsets(sourceLocation.end), srcId, - // @ts-ignore - ...options.sourcesMap?.get(srcId)?.getOffsets(sourceLocation.sta), sourceFileName, options.sourcesMap?.get(srcId)?.getContent?.()); + else { + if (sourceFileName != null && options.output != null && !sourceFileName.startsWith("data:")) { + if (cache[sourceFileName] == null) { + const absolute = options.resolve(dirname(options.output), options.cwd) + .absolute; + const absoluteSourceFileName = options.resolve(sourceFileName, options.cwd) + .absolute; + cache[sourceFileName] = options.resolve(absoluteSourceFileName, absolute).relative; + } + sourceFileName = cache[sourceFileName]; + } + sourcemaps.push([newLine, newColumn, srcId, ...offsets, sourceFileName, sourceContent]); + } } - move(sourceLocation, linesMap, str); + move(sourceLocation, linesMap, offset > 0 ? str.slice(offset) : str); } /** * Update position - * @param position + * @param sourceLocation + * @param linesMap * @param str */ function move(sourceLocation, linesMap, str) { @@ -24499,8 +24791,9 @@ function move(sourceLocation, linesMap, str) { * render ast node * @param data * @param options - * @param sourcemap - * @param position + * @param sourcemaps + * @param sourceLocation + * @param linesMap * @param errors * @param reducer * @param cache @@ -24509,13 +24802,17 @@ function move(sourceLocation, linesMap, str) { * * @internal */ -function renderAstNode(data, options, sourcemap, sourceLocation, linesMap, errors, reducer, cache, level = 0, indents = []) { +function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, errors, reducer, cache, level = 0, indents = []) { if (indents.length < level + 1) { indents.push(options.indent.repeat(level)); } if (indents.length < level + 2) { indents.push(options.indent.repeat(level + 1)); } + // @ts-ignore + let children = ""; + let str = ""; + let previousStr = ""; const indent = indents[level]; const indentSub = indents[level + 1]; switch (data.typ) { @@ -24533,20 +24830,17 @@ function renderAstNode(data, options, sourcemap, sourceLocation, linesMap, error ? data.val : ""; case exports.EnumToken.StyleSheetNodeType: - return data.chi.reduce((css, node) => { - const hasPreviousContent = css !== ""; - const str = renderAstNode(node, options, sourcemap, sourceLocation, linesMap, errors, reducer, cache, level, indents); + for (const node of data.chi) { + str = renderAstNode(node, options, sourcemaps, sourceLocation, linesMap, errors, reducer, cache, level, indents); if (str === "") { - return css; - } - if (sourcemap != null && node[LOC] != null) { - updateSourceMap(node, options, cache, sourcemap, sourceLocation, linesMap, (hasPreviousContent ? options.newLine : "") + str); + continue; } - if (!hasPreviousContent) { - return str; + if (children.length > 0) { + str = options.newLine + str; } - return `${css}${options.newLine}${str}`; - }, ""); + children += str; + } + return children; case exports.EnumToken.AtRuleNodeType: case exports.EnumToken.RuleNodeType: case exports.EnumToken.KeyFramesRuleNodeType: @@ -24554,9 +24848,15 @@ function renderAstNode(data, options, sourcemap, sourceLocation, linesMap, error if ([exports.EnumToken.AtRuleNodeType, exports.EnumToken.KeyframesAtRuleNodeType].includes(data.typ) && !("chi" in data)) { return `${indent}@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val};`; } - // @ts-ignore - let children = data.chi.reduce((css, node) => { - let str; + const prelude = [exports.EnumToken.AtRuleNodeType, exports.EnumToken.KeyframesAtRuleNodeType].includes(data.typ) + ? `@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val}${options.indent}{` + : data.sel + `${options.indent}{`; + if (sourcemaps != null) { + updateSourceMap(data, options, cache, sourcemaps, sourceLocation, linesMap, prelude); + } + let node; + for (let i = 0; i < data.chi.length; i++) { + node = data.chi[i]; if (node.typ == exports.EnumToken.CommentNodeType) { str = options.removeComments && @@ -24579,41 +24879,45 @@ function renderAstNode(data, options, sourcemap, sourceLocation, linesMap, error : node.val) .reduce(reducer, "") .trimEnd()};`; + if (sourcemaps != null) { + if (previousStr.length > 0) { + move(sourceLocation, linesMap, previousStr); + } + } + previousStr = str === "" ? "" : options.newLine + indentSub + str; } // else if (node.typ == EnumToken.AtRuleNodeType && !("chi" in node)) { // str = `${(node).val === "" ? "" : options.indent || " "}${(node).val};`; // } else { - str = renderAstNode(node, options, sourcemap, sourceLocation, linesMap, errors, reducer, cache, level + 1, indents); - } - if (css === "") { - return str; + if (sourcemaps != null) { + if (previousStr.length > 0) { + move(sourceLocation, linesMap, previousStr); + } + } + str = renderAstNode(node, options, sourcemaps, sourceLocation, linesMap, errors, reducer, cache, level + 1, indents); + previousStr = ""; } if (str === "") { - return css; + continue; } - return `${css}${options.newLine}${indentSub}${str}`; - }, ""); - if (options.removeEmpty && children === "") { - return ""; + str = options.newLine + indentSub + str; + children += str; + } + if (sourcemaps != null && str !== "") { + move(sourceLocation, linesMap, str.endsWith(";") ? str.slice(0, -1) : str); } if (children.endsWith(";")) { children = children.slice(0, -1); } - const rendered = [exports.EnumToken.AtRuleNodeType, exports.EnumToken.KeyframesAtRuleNodeType].includes(data.typ) - ? `@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val}${options.indent}{${options.newLine}` + - (children === "" ? "" : indentSub + children + options.newLine) + - indent + - `}` - : data.sel + - `${options.indent}{${options.newLine}` + - (children === "" ? "" : indentSub + children + options.newLine) + - indent + - `}`; - if (sourcemap != null && data[LOC] != null) { - updateSourceMap(data, options, cache, sourcemap, { ...sourceLocation }, linesMap.clone(), rendered); - } - return rendered; + if (options.removeEmpty && children === "") { + return ""; + } + const end = options.newLine + indent + `}`; + if (sourcemaps != null) { + move(sourceLocation, linesMap, end); + } + return prelude + children + end; // case EnumToken.CssVariableTokenType: // case EnumToken.CssVariableImportTokenType: // return `@value ${(data).val}:${options.indent}${filterValues( @@ -24640,6 +24944,9 @@ function renderAstNode(data, options, sourcemap, sourceLocation, linesMap, error * render ast token * @param token * @param options + * @param cache + * @param reducer + * @param errors * @private */ function renderValue(token, options = {}, cache = Object.create(null), reducer, errors) { @@ -28677,7 +28984,7 @@ function doParseSync(iter, options = {}) { if (replacement == null) { continue; } - if (replacement == null || replacement == node) { + if (replacement == node) { continue; } // @ts-ignore @@ -28723,7 +29030,7 @@ function doParseSync(iter, options = {}) { if (replacement == null) { continue; } - if (replacement == null || replacement == node) { + if (replacement == node) { continue; } // @ts-ignore @@ -28757,7 +29064,7 @@ function doParseSync(iter, options = {}) { if (replacement == null) { continue; } - if (replacement != null && replacement != node) { + if (replacement != node) { node = replacement; } } @@ -28790,7 +29097,7 @@ function doParseSync(iter, options = {}) { if (result == null) { continue; } - if (result != null && result != node) { + if (result != node) { node = result; } if (Array.isArray(node)) { @@ -28975,10 +29282,9 @@ function doParseSync(iter, options = {}) { if (node.typ == exports.EnumToken.DeclarationNodeType) { if (node.nam.startsWith("--")) { if (!(node.nam in namesMapping)) { - let result = moduleSettings.scoped & exports.ModuleScopeEnumOptions.Global + let value = moduleSettings.scoped & exports.ModuleScopeEnumOptions.Global ? node.nam : moduleSettings.generateScopedName(node.nam, moduleSettings.filePath, moduleSettings.pattern, moduleSettings.hashLength); - let value = result; mapping[node.nam] = "--" + (moduleSettings.naming & exports.ModuleCaseTransformEnum.DashCaseOnly || @@ -29020,10 +29326,9 @@ function doParseSync(iter, options = {}) { continue; } if (!(rule.val in mapping)) { - let result = moduleSettings.scoped & exports.ModuleScopeEnumOptions.Global + let value = moduleSettings.scoped & exports.ModuleScopeEnumOptions.Global ? rule.val : moduleSettings.generateScopedName(rule.val, moduleSettings.filePath, moduleSettings.pattern, moduleSettings.hashLength); - let value = result; mapping[rule.val] = (rule.typ == exports.EnumToken.DashedIdenTokenType ? "--" : "") + (moduleSettings.naming & exports.ModuleCaseTransformEnum.DashCaseOnly || @@ -29194,10 +29499,10 @@ function doParseSync(iter, options = {}) { "unset", ].includes(value.val)) { if (!(value.val in mapping)) { - const result = moduleSettings.scoped & exports.ModuleScopeEnumOptions.Global - ? value.val - : moduleSettings.generateScopedName(value.val, moduleSettings.filePath, moduleSettings.pattern, moduleSettings.hashLength); - mapping[value.val] = result; + mapping[value.val] = + moduleSettings.scoped & exports.ModuleScopeEnumOptions.Global + ? value.val + : moduleSettings.generateScopedName(value.val, moduleSettings.filePath, moduleSettings.pattern, moduleSettings.hashLength); revMapping[mapping[value.val]] = value.val; } value.val = mapping[value.val]; @@ -29269,10 +29574,9 @@ function doParseSync(iter, options = {}) { if (value.typ == exports.EnumToken.ClassSelectorTokenType) { const val = value.val.slice(1); if (!(val in mapping)) { - const result = moduleSettings.scoped & exports.ModuleScopeEnumOptions.Global + let value = moduleSettings.scoped & exports.ModuleScopeEnumOptions.Global ? val : moduleSettings.generateScopedName(val, moduleSettings.filePath, moduleSettings.pattern, moduleSettings.hashLength); - let value = result; mapping[val] = moduleSettings.naming & exports.ModuleCaseTransformEnum.DashCaseOnly || moduleSettings.naming & exports.ModuleCaseTransformEnum.CamelCaseOnly @@ -29305,10 +29609,9 @@ function doParseSync(iter, options = {}) { if ((prefix == "--" && value.typ == exports.EnumToken.DashedIdenTokenType) || (prefix == "" && value.typ == exports.EnumToken.IdenTokenType)) { if (!(value.val in mapping)) { - const result = moduleSettings.scoped & exports.ModuleScopeEnumOptions.Global + let val = moduleSettings.scoped & exports.ModuleScopeEnumOptions.Global ? value.val : moduleSettings.generateScopedName(value.val, moduleSettings.filePath, moduleSettings.pattern, moduleSettings.hashLength); - let val = result; mapping[value.val] = prefix + (moduleSettings.naming & exports.ModuleCaseTransformEnum.DashCaseOnly || @@ -29658,7 +29961,7 @@ async function doParse(iter, options = {}) { const token = node[TOKENS][0]; const url = token.typ == exports.EnumToken.StringTokenType ? token.val.slice(1, -1) : token.val; try { - const src = options.resolve(url, options.src || options.cwd); + const src = options.resolve(url, options.src ? dirname(options.src) : options.cwd); const result = options.load(src); const stream = result instanceof Promise || Object.getPrototypeOf(result).constructor.name == "AsyncFunction" ? await result @@ -30132,6 +30435,7 @@ async function doParse(iter, options = {}) { parentRule.chi.splice(parentRule.chi.indexOf(node), 1); continue; } + const resolvedSrc = options.resolve(options.src, options.cwd); for (const token of composeSelectors) { // composes: a b c; if (token.r == null) { @@ -30201,8 +30505,10 @@ async function doParse(iter, options = {}) { setParent: false, src: src.relative, })); - const srcIndex = (src.relative.startsWith("/") || src.relative.startsWith("../") ? "" : "./") + - src.relative; + let srcIndex = options.resolve(src.absolute, resolvedSrc.absolute).relative; + if (!srcIndex.startsWith("/") && !srcIndex.startsWith("../")) { + srcIndex = `./${srcIndex}`; + } if (Object.keys(root.mapping).length > 0) { importMapping[srcIndex] = {}; } @@ -30454,26 +30760,6 @@ async function doParse(iter, options = {}) { exports.EnumToken.DescendantCombinatorTokenType) { parent[TOKENS].splice(index, 1); } - // if (val == ":global") { - // for (; index < (parent as AstRule)[TOKENS]!.length; index++) { - // if ( - // (parent as AstRule)[TOKENS]![index].typ == - // EnumToken.CommaTokenType || - // ([ - // EnumToken.PseudoClassFuncTokenType, - // EnumToken.PseudoClassTokenType, - // ].includes((parent as AstRule)[TOKENS]![index].typ) && - // [":global", ":local"].includes( - // ( - // (parent as AstRule)[TOKENS]![index] as PseudoClassToken - // ).val.toLowerCase(), - // )) - // ) { - // break; - // } - // global.add((parent as AstRule)[TOKENS]![index]); - // } - // } } break; } @@ -30487,12 +30773,6 @@ async function doParse(iter, options = {}) { case ":local": parent[TOKENS].splice(parent[TOKENS].indexOf(value), 1, ...value.chi); break; - // (parent as AstRule)[TOKENS]!.splice( - // (parent as AstRule)[TOKENS]!.indexOf(value), - // 1, - // ...(value as FunctionToken).chi, - // ); - // break; } } })) { @@ -30778,6 +31058,8 @@ function parseNode(tokens, context, options, errors, stats, invalidNodes) { return null; } /** + * @param stream + * @param context * @param options * @param errors * @param parseAsBlock @@ -30854,7 +31136,6 @@ function parseAtRule(stream, context, options, errors, parseAsBlock = null) { parseAsBlock = blockAllowed; } if (syntax != null && atRule.nam !== "layer" && parseAsBlock !== blockAllowed) { - success = false; errors.push({ action: "drop", node: atRule, @@ -31342,7 +31623,7 @@ function parseAtRule(stream, context, options, errors, parseAsBlock = null) { action: "drop", node: atRule, location: options.source.getSourceLocation(atRule[LOC].sta), - message: "node is allowd only in @page rule", + message: "node is allowed only in @page rule", }); } else { @@ -31481,9 +31762,6 @@ function parseAtRule(stream, context, options, errors, parseAsBlock = null) { if (result.errors.length > 0) { errors.push(...result.errors); } - // else if (atRuleName === "document") { - // parseUrlToken(stream); - // } if (result.success) { let i = 0; const stack = []; @@ -31827,6 +32105,52 @@ exports.ResponseType = void 0; ResponseType[ResponseType["ArrayBuffer"] = 2] = "ArrayBuffer"; })(exports.ResponseType || (exports.ResponseType = {})); +/** + * parse result. process input sourcemap + * @param result + * @param options + * @returns + * @private + */ +function parseResult(result, options) { + if (options.sourcemap != null && options.source.getInputSourceMap() == null) { + if (options.inputSourceMap != null) { + options.source.setInputSourceMap(options.inputSourceMap); + } + else { + // extract inline source map from the input CSS + const token = result.ast.chi.at(-1); + if (token?.typ == exports.EnumToken.CommentTokenType && + token.val.startsWith("/*# sourceMappingURL=")) { + const data = token.val.slice(21, -2).trim(); + let sourcemap; + let encoding = ""; + if (data.startsWith("data:")) { + let offset = data.indexOf(",") + 1; + if (offset == 0) { + offset = data.lastIndexOf(";") + 1; + } + else { + encoding = data.slice(data.lastIndexOf(";") + 1, offset - 1); + } + if (encoding == "base64") { + sourcemap = atob(data.slice(offset)); + } + else { + sourcemap = decodeURIComponent(data.slice(offset)); + } + options.source.setInputSourceMap(sourcemap); + } + } + } + } + if (options.module) { + const { revMapping, ...res } = result; + return res; + } + return result; +} + /** * Load file or url * @param url @@ -31965,7 +32289,7 @@ function parseSync(...args) { } options ??= {}; options.src ??= ""; - options.sourcesMap ??= new Map; + options.sourcesMap ??= new Map(); Object.assign(options, { resolve, dirname, @@ -31988,13 +32312,10 @@ function parseSync(...args) { currentPosition: -1, }; const result = doParseSync(tokenize(options.parseInfo), options); - const { revMapping, ...res } = result; - return res; + return !options.module && !options.inputSourceMap ? result : parseResult(result, options); } /** * Transform css - * @param css - * @param options * * ```ts * @@ -32005,6 +32326,7 @@ function parseSync(...args) { * console.log(result.code); * ``` * + * @param args */ function transformSync(...args) { let options; @@ -32116,7 +32438,7 @@ async function parse(...args) { } options ??= {}; options.src ??= ""; - options.sourcesMap ??= new Map; + options.sourcesMap ??= new Map(); Object.assign(options, { load, resolve, @@ -32139,10 +32461,7 @@ async function parse(...args) { position: 0, currentPosition: -1, }; - return doParse(stream instanceof ReadableStream ? tokenizeStream(stream, options.parseInfo) : tokenize(options.parseInfo), options).then((result) => { - const { revMapping, ...res } = result; - return res; - }); + return doParse(stream instanceof ReadableStream ? tokenizeStream(stream, options.parseInfo) : tokenize(options.parseInfo), options).then((result) => (!options.module && !options.inputSourceMap ? result : parseResult(result, options))); } /** * Transform css file @@ -32175,8 +32494,6 @@ const transformFile = node_util.deprecate(async (file, options = {}, asStream = }), "transformFile is deprecated, use transform instead as transform({file, asStream, ...options})"); /** * Transform css - * @param css - * @param options * * Parsing a string * @@ -32215,6 +32532,7 @@ const transformFile = node_util.deprecate(async (file, options = {}, asStream = * * console.log(result.code); * ``` + * @param args */ async function transform(...args) { let options; diff --git a/dist/index.d.ts b/dist/index.d.ts index c60324b9..22524cb1 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -3628,10 +3628,14 @@ export declare interface VisitorNodeMap { } /** - * Source map class - * @internal + * Generate and parse source map */ declare class SourceMap { + /** + * + * @private + */ + private keys; /** * Last location */ @@ -3646,27 +3650,57 @@ declare class SourceMap { * @private */ private sourcesMap; + /** + * Sources content + * @private + */ + private readonly sourcesContent; /** * Sources * @private */ - private sources; + private readonly sources; /** * Map * @private + * */ private map; + /** + * Map + * @private + * + */ + private reverseMap; /** * Line * @private */ private line; /** - * Add a location - * @param source - * @param original + * */ - add(newLine: number, newColumn: number, srcId: number, ln: number, col: number, sourceFileName: string, sourceContent: string): void; + constructor(); + /** + * + * @param sourcemaps + */ + constructor(sourcemaps: string | SourceMapObject); + /** + * Add all location + * @param maps + */ + addAll(maps: Array<[number, number, number, number, number, string | null, string | null]>): void; + /** + * compute original positions + */ + computePositions(): void; + /** + * retrieve original sources, lines and columns + * @param line generated line + * @param column generated column + */ + find(line: number, column: number): Array<[string | null, number, number, string | null]> | null; /** * Convert to URL encoded string */ @@ -3675,6 +3709,10 @@ declare class SourceMap { * Convert to JSON object */ toJSON(): SourceMapObject; + /** + * to string + */ + toString(): string; } /** @@ -3689,7 +3727,7 @@ declare class LineMap { * Constructor * @param lines */ - constructor(lines: number[]); + constructor(lines?: number[]); /** * Compute line and column of the offset * @param offset @@ -3722,6 +3760,7 @@ declare class LineMap { * Source file helper class */ declare class SourceFile { + private inputSourceMap; /** * Source file ID */ @@ -3740,7 +3779,6 @@ declare class SourceFile { private content; /** * Constructor - * @param id * @param content * @param lines * @param file @@ -3749,7 +3787,6 @@ declare class SourceFile { /** * Update source content * @param content - * @param lines */ append(content: string): void; /** @@ -3791,6 +3828,16 @@ declare class SourceFile { * @param lineStart */ addLineStart(lineStart: number): void; + /** + * set input source map + * @param inputSourceMap + */ + setInputSourceMap(inputSourceMap: SourceMapObject | string | null): void; + /** + * return input source map + * @returns + */ + getInputSourceMap(): SourceMap | null; } export declare interface PropertyListOptions { @@ -5040,21 +5087,38 @@ export declare interface ParseInputStreamOptions { input: string | ReadableStream; } +/** + * Input options for string or stream + * @internal + */ export declare interface ParseSourceOptions { sourcesMap?: Map; source?: SourceFile | null; } +export declare interface ParserSourceMapOptions { + /** + * Include sourcemap in the ast. Sourcemap info is always generated + */ + sourcemap?: boolean | "inline"; + /** + * Input source map + */ + inputSourceMap?: SourceMapObject | string; +} + export declare interface ParserSyncOptions - extends MinifyOptions, MinifyFeatureOptions, ValidationOptions, PropertyListOptions, ParseSourceOptions { + extends + MinifyOptions, + ParserSourceMapOptions, + MinifyFeatureOptions, + ValidationOptions, + PropertyListOptions, + ParseSourceOptions { /** * Source file to be used for sourcemap */ src?: string; - /** - * Include sourcemap in the ast. Sourcemap info is always generated - */ - sourcemap?: boolean | "inline"; /** * Remove at-rule charset */ @@ -5256,6 +5320,11 @@ export declare interface ResolvedPath { * Ast node render options */ export declare interface RenderOptions { + /** + * Source file to be used as CSS input file for sourcemap resolution + */ + src?: string; + /** * Minify css values. */ @@ -5793,6 +5862,9 @@ declare function parseString(src: string, options?: { * render ast token * @param token * @param options + * @param cache + * @param reducer + * @param errors * @private */ declare function renderValue(token: Token$1, options?: RenderOptions, cache?: { @@ -5833,7 +5905,7 @@ declare function okLabDistance(color1: ColorToken, color2: ColorToken): number | declare function isOkLabClose(color1: ColorToken, color2: ColorToken, threshold?: number): boolean; /** - * Search the ast sub-tree and return the first match + * Search the ast subtree and return the first match * * ```ts * // find the first ast declaration node which name is 'aspect-ratio' @@ -6085,7 +6157,6 @@ declare const parseFile: (file: string, options?: ParserOptions, asStream?: bool declare function parseSync(stream: string, options?: ParserSyncOptions): ParseResult; /** * Parse css string - * @param stream * @param options * * Parsing a string @@ -6181,7 +6252,6 @@ declare function transformSync(options: ParseInputOptions & TransformSyncOptions declare function parse(stream: string | ReadableStream, options?: ParserOptions): Promise; /** * Parse css - * @param stream * @param options * * @throws Error file not found @@ -6214,7 +6284,6 @@ declare function parse(stream: string | ReadableStream, options?: Pa declare function parse(options: ParseInputFileOptions & ParserOptions): Promise; /** * Parse css - * @param stream * @param options * * Parsing a string @@ -6325,7 +6394,6 @@ declare const transformFile: (file: string, options?: TransformOptions, asStream declare function transform(css: string | ReadableStream, options?: TransformOptions): Promise; /** * Transform css - * @param css * @param options * * Parsing a string @@ -6369,7 +6437,6 @@ declare function transform(css: string | ReadableStream, options?: T declare function transform(options: ParseInputStreamOptions & TransformOptions): Promise; /** * Transform css - * @param css * @param options * * Parsing a string @@ -6412,4 +6479,4 @@ declare function transform(options: ParseInputStreamOptions & TransformOptions): declare function transform(options: ParseInputFileOptions & TransformOptions): Promise; export { ColorType$1 as ColorType, EnumAstNodeStatus$1 as EnumAstNodeStatus, EnumToken, FeatureWalkMode, ModuleCaseTransformEnum, ModuleScopeEnumOptions, ResponseType$1 as ResponseType, SourceMap, ValidationLevel, WalkerEvent, WalkerOptionEnum, cloneNode, convertColor, dirname, expand, find, findAll, findByValue, findLast, isOkLabClose, load, minify, okLabDistance, parse, parseDeclarations, parseFile, parseString, parseSync, render, renderValue as renderToken, replaceNodeOrValue, resolve, transform, transformFile, transformSync, walk, walkValues }; -export type { AddToken, AndToken, AngleToken, AstAtRule, AstComment, AstDeclaration, AstInvalidAtRule, AstInvalidDeclaration, AstInvalidRule, AstKeyFrameRule, AstKeyframesAtRule, AstKeyframesRule, AstNode$1 as AstNode, AstNodeStatus, AstRule, AstRuleList, AstStyleSheet, AstValueMatcher, AtRuleToken, AtRuleVisitorHandler, AttrEndToken, AttrStartToken, AttrToken, Background, BackgroundAttachmentMapping, BackgroundPosition, BackgroundPositionClass, BackgroundPositionConstraints, BackgroundPositionMapping, BackgroundProperties, BackgroundRepeat, BackgroundRepeatMapping, BackgroundSize, BackgroundSizeMapping, BadCDOCommentToken, BadCommentToken, BadStringToken, BadUrlToken, BaseToken, BinaryExpressionNode, BinaryExpressionToken, BlockEndToken, BlockStartToken, Border, BorderColor, BorderColorClass, BorderProperties, BorderRadius, CDOCommentToken, ChildCombinatorToken, ClassSelectorToken, ColonToken, ColorToken, ColumnCombinatorToken, CommaToken, CommentToken, ComposesSelectorToken, ConstraintsMapping, ContainMatchToken, ContainerStyleRangeToken, Context, CssVariableImportTokenType$1 as CssVariableImportTokenType, CssVariableMapTokenType, CssVariableToken$1 as CssVariableToken, DashMatchToken, DashedIdentToken, DeclarationVisitorHandler, DelimToken, DescendantCombinatorToken, DimensionToken, DivToken, DoubleColonToken, EOFToken, EndMatchToken, EqualMatchToken, ErrorDescription$1 as ErrorDescription, FlexToken, Font, FontFamily, FontProperties, FontWeight, FontWeightConstraints, FontWeightMapping, FractionToken, FrequencyToken, FunctionDefToken, FunctionImageToken, FunctionToken, FunctionURLToken, GenericVisitorAstNodeHandlerMap, GenericVisitorAstNodeSyncHandlerMap, GenericVisitorAsyncResult, GenericVisitorHandler, GenericVisitorResult, GenericVisitorSyncHandler, GenericVisitorSyncResult, GreaterThanOrEqualToken, GreaterThanToken, GridTemplateFuncToken, HashToken, IdentListToken, IdentToken, IfConditionToken, IfElseConditionToken, ImportantToken, IncludeMatchToken, InvalidAttrToken, InvalidClassSelectorToken, InvalidMediaQueryToken, LengthToken, LessThanOrEqualToken, LessThanToken, LineHeight, ListToken, LiteralToken, LoadResult, Map$1 as Map, MatchExpressionToken, MatchedSelector, MediaFeatureOnlyToken, MediaFeatureToken, MediaQueryConditionToken, MediaQueryUnaryFeatureToken, MediaRangeQueryToken, MinifyFeature, MinifyFeatureOptions, MinifyOptions, ModuleAsyncOptions, ModuleSyncOptions, MulToken, NameSpaceAttributeToken, NestingSelectorToken, NextSiblingCombinatorToken, NotToken, NumberToken, OptimizedSelector, OptimizedSelectorToken, OrToken, Outline, OutlineProperties, ParensEndToken, ParensStartToken, ParensToken, ParseInfo$1 as ParseInfo, ParseInputFileOptions, ParseInputOptions, ParseInputStreamOptions, ParseResult, ParseResultStats, ParseSourceOptions, ParseTokenOptions, ParserOptions, ParserSyncOptions, PercentageToken, Prefix, PropertiesConfig, PropertiesConfigProperties, PropertyListOptions, PropertyMapType, PropertySetType, PropertyType, PseudoClassFunctionToken, PseudoClassToken, PseudoElementToken, PseudoPageToken, PurpleBackgroundAttachment, RawNodeToken, RawSelectorTokens, RenderOptions, RenderResult, ResolutionToken, ResolvedPath, RuleVisitorHandler, SemiColonToken, Separator, ShorthandDef, ShorthandMapType, ShorthandProperties, ShorthandPropertyType, ShorthandType, SinglePropertyType, SinglePropertyTypeMapping, SourceLocation, SourceMapObject, StartMatchToken, StringToken, SubToken, SubsequentCombinatorToken, SupportsQueryConditionToken, SupportsQueryUnaryConditionToken, TimeToken, TimelineFunctionToken, TimingFunctionToken, Token$1 as Token, TokenSearchResult, TokenizeResult, TransformOptions, TransformResult, TransformSyncOptions, UnaryExpression, UnaryExpressionNode, UnclosedStringToken, UniversalSelectorToken, UrlToken, ValidationConfiguration, ValidationMediaFeature, ValidationOptions, ValidationResult, ValidationSelectorOptions, ValidationSyntaxNode, ValidationSyntaxResult, ValidationToken$1 as ValidationToken, Value, ValueVisitorHandler, ValueVisitorSyncHandler, VariableScopeInfo, VisitorNodeMap, VisitorSyncNodeMap, WalkAttributesResult, WalkResult, WalkerFilter, WalkerOption, WalkerValueFilter, WhenElseQueryConditionToken, WhenElseUnaryConditionToken, WhitespaceToken, WrappedValuesToken }; +export type { AddToken, AndToken, AngleToken, AstAtRule, AstComment, AstDeclaration, AstInvalidAtRule, AstInvalidDeclaration, AstInvalidRule, AstKeyFrameRule, AstKeyframesAtRule, AstKeyframesRule, AstNode$1 as AstNode, AstNodeStatus, AstRule, AstRuleList, AstStyleSheet, AstValueMatcher, AtRuleToken, AtRuleVisitorHandler, AttrEndToken, AttrStartToken, AttrToken, Background, BackgroundAttachmentMapping, BackgroundPosition, BackgroundPositionClass, BackgroundPositionConstraints, BackgroundPositionMapping, BackgroundProperties, BackgroundRepeat, BackgroundRepeatMapping, BackgroundSize, BackgroundSizeMapping, BadCDOCommentToken, BadCommentToken, BadStringToken, BadUrlToken, BaseToken, BinaryExpressionNode, BinaryExpressionToken, BlockEndToken, BlockStartToken, Border, BorderColor, BorderColorClass, BorderProperties, BorderRadius, CDOCommentToken, ChildCombinatorToken, ClassSelectorToken, ColonToken, ColorToken, ColumnCombinatorToken, CommaToken, CommentToken, ComposesSelectorToken, ConstraintsMapping, ContainMatchToken, ContainerStyleRangeToken, Context, CssVariableImportTokenType$1 as CssVariableImportTokenType, CssVariableMapTokenType, CssVariableToken$1 as CssVariableToken, DashMatchToken, DashedIdentToken, DeclarationVisitorHandler, DelimToken, DescendantCombinatorToken, DimensionToken, DivToken, DoubleColonToken, EOFToken, EndMatchToken, EqualMatchToken, ErrorDescription$1 as ErrorDescription, FlexToken, Font, FontFamily, FontProperties, FontWeight, FontWeightConstraints, FontWeightMapping, FractionToken, FrequencyToken, FunctionDefToken, FunctionImageToken, FunctionToken, FunctionURLToken, GenericVisitorAstNodeHandlerMap, GenericVisitorAstNodeSyncHandlerMap, GenericVisitorAsyncResult, GenericVisitorHandler, GenericVisitorResult, GenericVisitorSyncHandler, GenericVisitorSyncResult, GreaterThanOrEqualToken, GreaterThanToken, GridTemplateFuncToken, HashToken, IdentListToken, IdentToken, IfConditionToken, IfElseConditionToken, ImportantToken, IncludeMatchToken, InvalidAttrToken, InvalidClassSelectorToken, InvalidMediaQueryToken, LengthToken, LessThanOrEqualToken, LessThanToken, LineHeight, ListToken, LiteralToken, LoadResult, Map$1 as Map, MatchExpressionToken, MatchedSelector, MediaFeatureOnlyToken, MediaFeatureToken, MediaQueryConditionToken, MediaQueryUnaryFeatureToken, MediaRangeQueryToken, MinifyFeature, MinifyFeatureOptions, MinifyOptions, ModuleAsyncOptions, ModuleSyncOptions, MulToken, NameSpaceAttributeToken, NestingSelectorToken, NextSiblingCombinatorToken, NotToken, NumberToken, OptimizedSelector, OptimizedSelectorToken, OrToken, Outline, OutlineProperties, ParensEndToken, ParensStartToken, ParensToken, ParseInfo$1 as ParseInfo, ParseInputFileOptions, ParseInputOptions, ParseInputStreamOptions, ParseResult, ParseResultStats, ParseSourceOptions, ParseTokenOptions, ParserOptions, ParserSourceMapOptions, ParserSyncOptions, PercentageToken, Prefix, PropertiesConfig, PropertiesConfigProperties, PropertyListOptions, PropertyMapType, PropertySetType, PropertyType, PseudoClassFunctionToken, PseudoClassToken, PseudoElementToken, PseudoPageToken, PurpleBackgroundAttachment, RawNodeToken, RawSelectorTokens, RenderOptions, RenderResult, ResolutionToken, ResolvedPath, RuleVisitorHandler, SemiColonToken, Separator, ShorthandDef, ShorthandMapType, ShorthandProperties, ShorthandPropertyType, ShorthandType, SinglePropertyType, SinglePropertyTypeMapping, SourceLocation, SourceMapObject, StartMatchToken, StringToken, SubToken, SubsequentCombinatorToken, SupportsQueryConditionToken, SupportsQueryUnaryConditionToken, TimeToken, TimelineFunctionToken, TimingFunctionToken, Token$1 as Token, TokenSearchResult, TokenizeResult, TransformOptions, TransformResult, TransformSyncOptions, UnaryExpression, UnaryExpressionNode, UnclosedStringToken, UniversalSelectorToken, UrlToken, ValidationConfiguration, ValidationMediaFeature, ValidationOptions, ValidationResult, ValidationSelectorOptions, ValidationSyntaxNode, ValidationSyntaxResult, ValidationToken$1 as ValidationToken, Value, ValueVisitorHandler, ValueVisitorSyncHandler, VariableScopeInfo, VisitorNodeMap, VisitorSyncNodeMap, WalkAttributesResult, WalkResult, WalkerFilter, WalkerOption, WalkerValueFilter, WhenElseQueryConditionToken, WhenElseUnaryConditionToken, WhitespaceToken, WrappedValuesToken }; diff --git a/dist/lib/ast/expand.js b/dist/lib/ast/expand.js index 3b4461d3..577eefeb 100644 --- a/dist/lib/ast/expand.js +++ b/dist/lib/ast/expand.js @@ -57,7 +57,10 @@ function expandRule(node) { ast.chi.splice(i--, 1); continue; } - selRule.forEach((arr) => combinators.includes(arr[0].charAt(0)) ? arr.unshift(arSelf) : arr.unshift(arSelf, " ")); + for (let i1 = 0; i1 < selRule.length; i1++) { + const arr = selRule[i1]; + combinators.includes(arr[0].charAt(0)) ? arr.unshift(arSelf) : arr.unshift(arSelf, " "); + } rule.sel = selRule .reduce((acc, curr) => { acc.push(curr.join("")); diff --git a/dist/lib/ast/find.js b/dist/lib/ast/find.js index 57b3cdb8..d70ac623 100644 --- a/dist/lib/ast/find.js +++ b/dist/lib/ast/find.js @@ -3,7 +3,7 @@ import { walk, walkValues } from './walk.js'; import { TOKENS } from '../syntax/constants.js'; /** - * Search the ast sub-tree and return the first match + * Search the ast subtree and return the first match * * ```ts * // find the first ast declaration node which name is 'aspect-ratio' diff --git a/dist/lib/ast/minify.js b/dist/lib/ast/minify.js index dc44fd30..5830747b 100644 --- a/dist/lib/ast/minify.js +++ b/dist/lib/ast/minify.js @@ -29,13 +29,16 @@ const features = Object.values(index).sort((a, b) => a.ordering - b.ordering); * @param errors * @param nestingContent * + * @param context * @private */ -function minify(ast, options = {}, recursive = false, errors, nestingContent, context = {}) { +function minify(ast, opt = {}, recursive = false, errors, nestingContent, context = {}) { let preprocess = false; let postprocess = false; let parents; let replacement; + // @ts-ignore + let { sourcemap, module, ...options } = opt; if (!("features" in options)) { // @ts-ignore options = { @@ -232,9 +235,9 @@ function transformAtRuleMediaPrelude(values) { * Minify at-rule media * - remove redundant tokens * - generate range queries - * @param ast * * @private + * @param tokens */ function minifyAtRuleMedia(tokens) { let hasUpdates = false; @@ -332,7 +335,6 @@ function doMinify(ast, options = {}, recursive = false, errors, nestingContent, } while (previous?.typ === EnumToken.CommentNodeType) { previous = ast.chi[--nodeIndex]; - continue; } node = ast.chi[i]; if (node.typ === EnumToken.AtRuleNodeType && node.nam === "font-face") { @@ -803,7 +805,9 @@ function optimizeSelector(selector) { } break; } - selector.forEach((selector) => selector.splice(0, optimized.length)); + for (let i1 = 0; i1 < selector.length; i1++) { + selector[i1].splice(0, optimized.length); + } let reducible = optimized.length == 1; if (optimized[0] == "&") { if (optimized[1] == " ") { @@ -1185,7 +1189,6 @@ function wrapNodes(previous, node, match, ast, reducer, i, nodeIndex) { * Diff nodes * @param n1 * @param n2 - * @param reducer * @param options * * @private @@ -1304,17 +1307,36 @@ function diff(n1, n2, options = {}) { // @ts-ignore chi: intersect.reverse(), }; + let op = { level: 0, ...options }; if (result == null || [n1, n2].reduce((acc, curr) => { let css = options.cache.get(curr); if (css == null) { - css = doRender(curr, options).code; + let level = 0; + let parent = curr[PARENT]; + while (parent != null && parent.typ != EnumToken.StyleSheetNodeType) { + level++; + parent = parent[PARENT]; + } + op.level = level; + css = doRender(curr, op).code; options.cache.set(curr, css); } return curr.chi.length == 0 ? acc : acc + css.length; }, 0) <= [node1, node2, result].reduce((acc, curr) => { - const css = doRender(curr, options).code; + let css = options.cache.get(curr); + if (css != null) { + return curr.chi.length == 0 ? acc : acc + css.length; + } + let level = 0; + let parent = curr[PARENT]; + while (parent != null && parent.typ != EnumToken.StyleSheetNodeType) { + level++; + parent = parent[PARENT]; + } + op.level = level; + css = doRender(curr, op).code; return curr.chi.length == 0 ? acc : acc + css.length; }, 0)) { if (node1.chi.length != 0 && node2.chi.length != 0) { diff --git a/dist/lib/fs/resolve.js b/dist/lib/fs/resolve.js index 9ae24cae..51da577d 100644 --- a/dist/lib/fs/resolve.js +++ b/dist/lib/fs/resolve.js @@ -1,5 +1,8 @@ import { memoize } from '../parser/utils/cache.js'; +/** + * match url + */ const matchUrl = /^(https?:)?\/\//; /** * return the directory name of a path @@ -11,6 +14,9 @@ function dirname(path) { if (path === "") { return ""; } + if (path.startsWith("data:")) { + return path; + } let i = 0; let parts = [""]; for (; i < path.length; i++) { @@ -34,10 +40,7 @@ function splitPath(result) { if (result.length == 0) { return { parts: [], i: 0 }; } - // if (result === "/") { - // return { parts: ["/"], i: 0 }; - // } - const parts = [""]; + const parts = result == "/" ? [] : [""]; let i = 0; for (; i < result.length; i++) { const chr = result.charAt(i); @@ -46,7 +49,7 @@ function splitPath(result) { } // else if (chr == "?" || chr == "#") { // break; - // } + // } else { parts[parts.length - 1] += chr; } @@ -64,6 +67,8 @@ function splitPath(result) { } /** * Nomalize path + * @param path + * @private */ const normalize = memoize(function (path) { let parts = []; @@ -92,14 +97,20 @@ const normalize = memoize(function (path) { while (++k < parts.length) { // if (parts[k] == ".") { // parts.splice(k--, 1); - // } else - if (parts[k] == "..") { + // } else + if (k > 0 && parts[k] == "..") { parts.splice(k - 1, 2); k -= 2; } } return (path.charAt(0) == "/" ? "/" : "") + parts.join("/"); }); +/** + * diff path + * @param path1 + * @param path2 + * @private + */ const diff = memoize(function (path1, path2) { let { parts } = splitPath(path1); const { parts: dirs } = splitPath(path2); @@ -131,30 +142,51 @@ const resolve = memoize(function (url, currentDirectory, cwd) { cwd ??= ""; currentDirectory ??= ""; url = normalize(url); + if (cwd !== "") { + cwd = normalize(cwd); + } if (currentDirectory !== "") { currentDirectory = normalize(currentDirectory); - if (url.startsWith(currentDirectory + "/")) { - return { - absolute: url, - relative: url.slice(currentDirectory.length + 1), - }; - } - } - if ((currentDirectory === "" || currentDirectory === ".") && cwd !== "") { - cwd = normalize(cwd); - if (url.startsWith(cwd == "/" ? cwd : cwd + "/")) { - const absolute = url; - const prefix = cwd == "/" ? cwd : cwd + "/"; - return { - absolute, - relative: absolute.startsWith(prefix) ? absolute.slice(prefix.length) : diff(absolute, cwd), - }; - } } + const dir = cwd || currentDirectory; + const absolute = dir == "" || url.startsWith("/") ? resolvePath(url) : resolvePath(dir, url); return { - absolute: url, - relative: url === "" ? "" : diff(url, cwd || currentDirectory), + absolute, + relative: dir === "" ? absolute : diff(absolute, dir), }; }); +/** + * + * @param parts + * @returns + * @private + */ +function resolvePath(...parts) { + const path = parts.filter(Boolean).join("/"); + const isAbsolute = /^[\\/]/.test(path); + const segments = path.split(/[\\/]+/); + const resolved = []; + for (const segment of segments) { + if (!segment || segment === ".") { + continue; + } + if (segment === "..") { + if (resolved.length && resolved[resolved.length - 1] !== "..") { + resolved.pop(); + } + else if (!isAbsolute) { + resolved.push(".."); + } + } + else { + resolved.push(segment); + } + } + let result = resolved.join("/"); + if (isAbsolute) { + result = "/" + result; + } + return result || (isAbsolute ? "/" : "."); +} export { diff, dirname, matchUrl, normalize, resolve }; diff --git a/dist/lib/parser/linesmap.js b/dist/lib/parser/linesmap.js index 0365942d..38daa8f7 100644 --- a/dist/lib/parser/linesmap.js +++ b/dist/lib/parser/linesmap.js @@ -10,7 +10,7 @@ class LineMap { * Constructor * @param lines */ - constructor(lines) { + constructor(lines = []) { if (lines.length === 0) { lines.push(0); } @@ -28,7 +28,7 @@ class LineMap { } const column = offset - this.lineStarts[line]; // [line, column] - return [line + 1, column === 0 ? 1 : column]; + return [line + 1, line === 0 ? column + 1 : column]; } /** * search the greatest index of the value less than or equal to offset diff --git a/dist/lib/parser/parse.js b/dist/lib/parser/parse.js index cd5df1f8..99b36382 100644 --- a/dist/lib/parser/parse.js +++ b/dist/lib/parser/parse.js @@ -24,6 +24,7 @@ import { parseAtRuleFontFeatureValues } from './utils/at-rule-font-feature-value import { matchGenericSyntax } from './utils/at-rule-generic.js'; import { memoize } from './utils/cache.js'; import { SourceFile } from './source.js'; +import { dirname } from '../fs/resolve.js'; function renderTokens(tokens, options) { if (tokens == null || tokens.length === 0) @@ -643,7 +644,7 @@ function doParseSync(iter, options = {}) { if (replacement == null) { continue; } - if (replacement == null || replacement == node) { + if (replacement == node) { continue; } // @ts-ignore @@ -689,7 +690,7 @@ function doParseSync(iter, options = {}) { if (replacement == null) { continue; } - if (replacement == null || replacement == node) { + if (replacement == node) { continue; } // @ts-ignore @@ -723,7 +724,7 @@ function doParseSync(iter, options = {}) { if (replacement == null) { continue; } - if (replacement != null && replacement != node) { + if (replacement != node) { node = replacement; } } @@ -756,7 +757,7 @@ function doParseSync(iter, options = {}) { if (result == null) { continue; } - if (result != null && result != node) { + if (result != node) { node = result; } if (Array.isArray(node)) { @@ -941,10 +942,9 @@ function doParseSync(iter, options = {}) { if (node.typ == EnumToken.DeclarationNodeType) { if (node.nam.startsWith("--")) { if (!(node.nam in namesMapping)) { - let result = moduleSettings.scoped & ModuleScopeEnumOptions.Global + let value = moduleSettings.scoped & ModuleScopeEnumOptions.Global ? node.nam : moduleSettings.generateScopedName(node.nam, moduleSettings.filePath, moduleSettings.pattern, moduleSettings.hashLength); - let value = result; mapping[node.nam] = "--" + (moduleSettings.naming & ModuleCaseTransformEnum.DashCaseOnly || @@ -986,10 +986,9 @@ function doParseSync(iter, options = {}) { continue; } if (!(rule.val in mapping)) { - let result = moduleSettings.scoped & ModuleScopeEnumOptions.Global + let value = moduleSettings.scoped & ModuleScopeEnumOptions.Global ? rule.val : moduleSettings.generateScopedName(rule.val, moduleSettings.filePath, moduleSettings.pattern, moduleSettings.hashLength); - let value = result; mapping[rule.val] = (rule.typ == EnumToken.DashedIdenTokenType ? "--" : "") + (moduleSettings.naming & ModuleCaseTransformEnum.DashCaseOnly || @@ -1160,10 +1159,10 @@ function doParseSync(iter, options = {}) { "unset", ].includes(value.val)) { if (!(value.val in mapping)) { - const result = moduleSettings.scoped & ModuleScopeEnumOptions.Global - ? value.val - : moduleSettings.generateScopedName(value.val, moduleSettings.filePath, moduleSettings.pattern, moduleSettings.hashLength); - mapping[value.val] = result; + mapping[value.val] = + moduleSettings.scoped & ModuleScopeEnumOptions.Global + ? value.val + : moduleSettings.generateScopedName(value.val, moduleSettings.filePath, moduleSettings.pattern, moduleSettings.hashLength); revMapping[mapping[value.val]] = value.val; } value.val = mapping[value.val]; @@ -1235,10 +1234,9 @@ function doParseSync(iter, options = {}) { if (value.typ == EnumToken.ClassSelectorTokenType) { const val = value.val.slice(1); if (!(val in mapping)) { - const result = moduleSettings.scoped & ModuleScopeEnumOptions.Global + let value = moduleSettings.scoped & ModuleScopeEnumOptions.Global ? val : moduleSettings.generateScopedName(val, moduleSettings.filePath, moduleSettings.pattern, moduleSettings.hashLength); - let value = result; mapping[val] = moduleSettings.naming & ModuleCaseTransformEnum.DashCaseOnly || moduleSettings.naming & ModuleCaseTransformEnum.CamelCaseOnly @@ -1271,10 +1269,9 @@ function doParseSync(iter, options = {}) { if ((prefix == "--" && value.typ == EnumToken.DashedIdenTokenType) || (prefix == "" && value.typ == EnumToken.IdenTokenType)) { if (!(value.val in mapping)) { - const result = moduleSettings.scoped & ModuleScopeEnumOptions.Global + let val = moduleSettings.scoped & ModuleScopeEnumOptions.Global ? value.val : moduleSettings.generateScopedName(value.val, moduleSettings.filePath, moduleSettings.pattern, moduleSettings.hashLength); - let val = result; mapping[value.val] = prefix + (moduleSettings.naming & ModuleCaseTransformEnum.DashCaseOnly || @@ -1624,7 +1621,7 @@ async function doParse(iter, options = {}) { const token = node[TOKENS][0]; const url = token.typ == EnumToken.StringTokenType ? token.val.slice(1, -1) : token.val; try { - const src = options.resolve(url, options.src || options.cwd); + const src = options.resolve(url, options.src ? dirname(options.src) : options.cwd); const result = options.load(src); const stream = result instanceof Promise || Object.getPrototypeOf(result).constructor.name == "AsyncFunction" ? await result @@ -2098,6 +2095,7 @@ async function doParse(iter, options = {}) { parentRule.chi.splice(parentRule.chi.indexOf(node), 1); continue; } + const resolvedSrc = options.resolve(options.src, options.cwd); for (const token of composeSelectors) { // composes: a b c; if (token.r == null) { @@ -2167,8 +2165,10 @@ async function doParse(iter, options = {}) { setParent: false, src: src.relative, })); - const srcIndex = (src.relative.startsWith("/") || src.relative.startsWith("../") ? "" : "./") + - src.relative; + let srcIndex = options.resolve(src.absolute, resolvedSrc.absolute).relative; + if (!srcIndex.startsWith("/") && !srcIndex.startsWith("../")) { + srcIndex = `./${srcIndex}`; + } if (Object.keys(root.mapping).length > 0) { importMapping[srcIndex] = {}; } @@ -2420,26 +2420,6 @@ async function doParse(iter, options = {}) { EnumToken.DescendantCombinatorTokenType) { parent[TOKENS].splice(index, 1); } - // if (val == ":global") { - // for (; index < (parent as AstRule)[TOKENS]!.length; index++) { - // if ( - // (parent as AstRule)[TOKENS]![index].typ == - // EnumToken.CommaTokenType || - // ([ - // EnumToken.PseudoClassFuncTokenType, - // EnumToken.PseudoClassTokenType, - // ].includes((parent as AstRule)[TOKENS]![index].typ) && - // [":global", ":local"].includes( - // ( - // (parent as AstRule)[TOKENS]![index] as PseudoClassToken - // ).val.toLowerCase(), - // )) - // ) { - // break; - // } - // global.add((parent as AstRule)[TOKENS]![index]); - // } - // } } break; } @@ -2453,12 +2433,6 @@ async function doParse(iter, options = {}) { case ":local": parent[TOKENS].splice(parent[TOKENS].indexOf(value), 1, ...value.chi); break; - // (parent as AstRule)[TOKENS]!.splice( - // (parent as AstRule)[TOKENS]!.indexOf(value), - // 1, - // ...(value as FunctionToken).chi, - // ); - // break; } } })) { @@ -2744,6 +2718,8 @@ function parseNode(tokens, context, options, errors, stats, invalidNodes) { return null; } /** + * @param stream + * @param context * @param options * @param errors * @param parseAsBlock @@ -2820,7 +2796,6 @@ function parseAtRule(stream, context, options, errors, parseAsBlock = null) { parseAsBlock = blockAllowed; } if (syntax != null && atRule.nam !== "layer" && parseAsBlock !== blockAllowed) { - success = false; errors.push({ action: "drop", node: atRule, @@ -3308,7 +3283,7 @@ function parseAtRule(stream, context, options, errors, parseAsBlock = null) { action: "drop", node: atRule, location: options.source.getSourceLocation(atRule[LOC].sta), - message: "node is allowd only in @page rule", + message: "node is allowed only in @page rule", }); } else { @@ -3447,9 +3422,6 @@ function parseAtRule(stream, context, options, errors, parseAsBlock = null) { if (result.errors.length > 0) { errors.push(...result.errors); } - // else if (atRuleName === "document") { - // parseUrlToken(stream); - // } if (result.success) { let i = 0; const stack = []; diff --git a/dist/lib/parser/source.js b/dist/lib/parser/source.js index 6f758722..04d7d7e0 100644 --- a/dist/lib/parser/source.js +++ b/dist/lib/parser/source.js @@ -1,3 +1,4 @@ +import { SourceMap } from '../renderer/sourcemap/sourcemap.js'; import { LineMap } from './linesmap.js'; /** @@ -8,6 +9,7 @@ let sourceId = 0; * Source file helper class */ class SourceFile { + inputSourceMap = null; /** * Source file ID */ @@ -26,7 +28,6 @@ class SourceFile { content; /** * Constructor - * @param id * @param content * @param lines * @param file @@ -40,7 +41,6 @@ class SourceFile { /** * Update source content * @param content - * @param lines */ append(content) { this.content += content; @@ -98,6 +98,20 @@ class SourceFile { addLineStart(lineStart) { this.lineStarts.addLineStart(lineStart); } + /** + * set input source map + * @param inputSourceMap + */ + setInputSourceMap(inputSourceMap) { + this.inputSourceMap = inputSourceMap == null ? null : new SourceMap(inputSourceMap); + } + /** + * return input source map + * @returns + */ + getInputSourceMap() { + return this.inputSourceMap; + } } export { SourceFile }; diff --git a/dist/lib/parser/tokenize.js b/dist/lib/parser/tokenize.js index e0cb381b..2145962b 100644 --- a/dist/lib/parser/tokenize.js +++ b/dist/lib/parser/tokenize.js @@ -378,7 +378,7 @@ function next(parseInfo, count = 1) { return char; } /** - * Tokenize css string + * Tokenize CSS string * @param parseInfo * @param yieldEOFToken */ @@ -405,8 +405,6 @@ function tokenize(parseInfo, yieldEOFToken = true) { parseInfo.buffer = ""; while ((value = peek(parseInfo))) { charCode = value.charCodeAt(0); - // nextCharCode = nextValue.charCodeAt(0); - // console.debug({value, buffer}); switch (charCode) { case 61 /* TokenMap.EQUALS */: if (buffer.length > 0) { @@ -771,10 +769,6 @@ function tokenize(parseInfo, yieldEOFToken = true) { break; } buffer += value + next(parseInfo); - // buffer += - // (parseInfo.offset == parseInfo.currentPosition - // ? parseInfo.buffer.slice(-1) - // : parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset - 1)) + value; break; case 39 /* TokenMap.SINGLE_QUOTE */: case 34 /* TokenMap.DOUBLE_QUOTE */: diff --git a/dist/lib/renderer/render.js b/dist/lib/renderer/render.js index 23d1108e..d96ed529 100644 --- a/dist/lib/renderer/render.js +++ b/dist/lib/renderer/render.js @@ -61,22 +61,28 @@ function doRender(data, options = {}, mapping) { const startTime = performance.now(); const errors = []; const sourcemap = options.sourcemap ? new SourceMap() : null; + const sourcemaps = options.sourcemap ? [] : null; const cache = Object.create(null); const sourceLocation = { - srcId: 0, - sta: 0, end: 0, }; - const linesMap = new LineMap([]); + const linesMap = options.sourcemap ? new LineMap() : null; let code = ""; if (mapping != null) { if (mapping.importMapping != null) { - for (const [key, value] of Object.entries(mapping.importMapping)) { + const absolutePath = options.resolve(options.output != null ? dirname(options.output) : dirname(options.src), options.cwd).absolute; + for (let [key, value] of Object.entries(mapping.importMapping)) { + key = options.resolve(options.resolve(key, options.cwd).absolute, absolutePath).relative; + if (!key.startsWith("/") && !key.startsWith(".")) { + key = "./" + key; + } code += `:import("${key}")${options.indent}{${options.newLine}${Object.entries(value).reduce((acc, [k, v]) => acc + (acc.length > 0 ? options.newLine : "") + `${options.indent}${v}:${options.indent}${k};`, "")}${options.newLine}}${options.newLine}`; } } code += `:export${options.indent}{${options.newLine}${Object.entries(mapping.mapping).reduce((acc, [k, v]) => acc + (acc.length > 0 ? options.newLine : "") + `${options.indent}${k}:${options.indent}${v};`, "")}${options.newLine}}${options.newLine}`; - move(sourceLocation, linesMap, code); + if (sourcemap != null) { + move(sourceLocation, linesMap, code); + } } if (options.output != null) { // @ts-ignore @@ -88,7 +94,7 @@ function doRender(data, options = {}, mapping) { [EnumToken.StyleSheetNodeType, EnumToken.AtRuleNodeType, EnumToken.RuleNodeType].includes(data.typ) && "chi" in data ? expand(data) - : data, options, sourcemap, sourceLocation, linesMap, errors, function reducer(acc, curr) { + : data, options, sourcemaps, sourceLocation, linesMap, errors, function reducer(acc, curr) { if (curr.typ == EnumToken.CommentTokenType && options.removeComments) { if (!options.preserveLicense || !curr.val.startsWith("/*!")) { return acc; @@ -103,6 +109,7 @@ function doRender(data, options = {}, mapping) { }, }; if (sourcemap != null) { + sourcemap.addAll(sourcemaps); result.map = sourcemap; if (options.sourcemap === "inline") { result.code += `\n/*# sourceMappingURL=${result.map.toUrl()} */`; @@ -115,37 +122,88 @@ function doRender(data, options = {}, mapping) { * @param node * @param options * @param cache - * @param sourcemap - * @param position + * @param sourcemaps + * @param sourceLocation + * @param linesMap * @param str * * @internal */ -function updateSourceMap(node, options, cache, sourcemap, sourceLocation, linesMap, str) { - if ([ - EnumToken.RuleNodeType, - EnumToken.AtRuleNodeType, - EnumToken.KeyFramesRuleNodeType, - EnumToken.KeyframesAtRuleNodeType, - ].includes(node.typ)) { - let srcId = node[LOC]?.srcId ?? 0; - let sourceFileName = options.sourcesMap?.get(srcId)?.getFileName?.() || null; - if (sourceFileName != null && options.output != null) { - if (cache[sourceFileName] == null) { - cache[sourceFileName] = options.resolve(sourceFileName, dirname(options.output)).relative; +function updateSourceMap(node, options, cache, sourcemaps, sourceLocation, linesMap, str) { + let offset = 0; + while (true) { + if (str.charAt(offset) == options.newLine) { + offset += options.newLine.length; + continue; + } + if (str.charAt(offset) == options.indent) { + offset += options.indent.length; + continue; + } + break; + } + if (offset > 0) { + move(sourceLocation, linesMap, str.slice(0, offset)); + } + if (node[LOC] != null && + [ + EnumToken.RuleNodeType, + EnumToken.AtRuleNodeType, + EnumToken.KeyFramesRuleNodeType, + EnumToken.KeyframesAtRuleNodeType, + ].includes(node.typ)) { + const source = options.sourcesMap.get(node[LOC].srcId); + const inputSourceMap = source.getInputSourceMap(); + const offsets = source.getOffsets(node[LOC].sta); + const [newLine, newColumn] = linesMap.getOffsets(sourceLocation.end); + let records = null; + let srcId = node[LOC].srcId; + let sourceFileName = source.getFileName() || null; + let sourceContent = source.getContent() || null; + if (inputSourceMap != null && (records = inputSourceMap.find(offsets[0], offsets[1])) != null) { + for (const record of records) { + // @ts-ignore + sourceFileName = record[0] || null; + // @ts-ignore + offsets[0] = record[1]; + // @ts-ignore + offsets[1] = record[2]; + sourceContent = record[3] || null; + if (sourceFileName != null && options.output != null && !sourceFileName.startsWith("data:")) { + if (cache[sourceFileName] == null) { + const absolute = options.resolve(dirname(options.output), options.cwd) + .absolute; + const absoluteSourcePath = options.resolve(dirname(options.src || ""), options.cwd).absolute; + // resolution is relative to the source file + const absoluteSourceFileName = options.resolve(sourceFileName, absoluteSourcePath) + .absolute; + cache[sourceFileName] = options.resolve(absoluteSourceFileName, absolute).relative; + } + sourceFileName = cache[sourceFileName]; + } + sourcemaps.push([newLine, newColumn, srcId, ...offsets, sourceFileName, sourceContent]); } - sourceFileName = cache[sourceFileName]; } - // @ts-ignore - sourcemap.add(...linesMap.getOffsets(sourceLocation.end), srcId, - // @ts-ignore - ...options.sourcesMap?.get(srcId)?.getOffsets(sourceLocation.sta), sourceFileName, options.sourcesMap?.get(srcId)?.getContent?.()); + else { + if (sourceFileName != null && options.output != null && !sourceFileName.startsWith("data:")) { + if (cache[sourceFileName] == null) { + const absolute = options.resolve(dirname(options.output), options.cwd) + .absolute; + const absoluteSourceFileName = options.resolve(sourceFileName, options.cwd) + .absolute; + cache[sourceFileName] = options.resolve(absoluteSourceFileName, absolute).relative; + } + sourceFileName = cache[sourceFileName]; + } + sourcemaps.push([newLine, newColumn, srcId, ...offsets, sourceFileName, sourceContent]); + } } - move(sourceLocation, linesMap, str); + move(sourceLocation, linesMap, offset > 0 ? str.slice(offset) : str); } /** * Update position - * @param position + * @param sourceLocation + * @param linesMap * @param str */ function move(sourceLocation, linesMap, str) { @@ -175,8 +233,9 @@ function move(sourceLocation, linesMap, str) { * render ast node * @param data * @param options - * @param sourcemap - * @param position + * @param sourcemaps + * @param sourceLocation + * @param linesMap * @param errors * @param reducer * @param cache @@ -185,13 +244,17 @@ function move(sourceLocation, linesMap, str) { * * @internal */ -function renderAstNode(data, options, sourcemap, sourceLocation, linesMap, errors, reducer, cache, level = 0, indents = []) { +function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, errors, reducer, cache, level = 0, indents = []) { if (indents.length < level + 1) { indents.push(options.indent.repeat(level)); } if (indents.length < level + 2) { indents.push(options.indent.repeat(level + 1)); } + // @ts-ignore + let children = ""; + let str = ""; + let previousStr = ""; const indent = indents[level]; const indentSub = indents[level + 1]; switch (data.typ) { @@ -209,20 +272,17 @@ function renderAstNode(data, options, sourcemap, sourceLocation, linesMap, error ? data.val : ""; case EnumToken.StyleSheetNodeType: - return data.chi.reduce((css, node) => { - const hasPreviousContent = css !== ""; - const str = renderAstNode(node, options, sourcemap, sourceLocation, linesMap, errors, reducer, cache, level, indents); + for (const node of data.chi) { + str = renderAstNode(node, options, sourcemaps, sourceLocation, linesMap, errors, reducer, cache, level, indents); if (str === "") { - return css; - } - if (sourcemap != null && node[LOC] != null) { - updateSourceMap(node, options, cache, sourcemap, sourceLocation, linesMap, (hasPreviousContent ? options.newLine : "") + str); + continue; } - if (!hasPreviousContent) { - return str; + if (children.length > 0) { + str = options.newLine + str; } - return `${css}${options.newLine}${str}`; - }, ""); + children += str; + } + return children; case EnumToken.AtRuleNodeType: case EnumToken.RuleNodeType: case EnumToken.KeyFramesRuleNodeType: @@ -230,9 +290,15 @@ function renderAstNode(data, options, sourcemap, sourceLocation, linesMap, error if ([EnumToken.AtRuleNodeType, EnumToken.KeyframesAtRuleNodeType].includes(data.typ) && !("chi" in data)) { return `${indent}@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val};`; } - // @ts-ignore - let children = data.chi.reduce((css, node) => { - let str; + const prelude = [EnumToken.AtRuleNodeType, EnumToken.KeyframesAtRuleNodeType].includes(data.typ) + ? `@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val}${options.indent}{` + : data.sel + `${options.indent}{`; + if (sourcemaps != null) { + updateSourceMap(data, options, cache, sourcemaps, sourceLocation, linesMap, prelude); + } + let node; + for (let i = 0; i < data.chi.length; i++) { + node = data.chi[i]; if (node.typ == EnumToken.CommentNodeType) { str = options.removeComments && @@ -255,41 +321,45 @@ function renderAstNode(data, options, sourcemap, sourceLocation, linesMap, error : node.val) .reduce(reducer, "") .trimEnd()};`; + if (sourcemaps != null) { + if (previousStr.length > 0) { + move(sourceLocation, linesMap, previousStr); + } + } + previousStr = str === "" ? "" : options.newLine + indentSub + str; } // else if (node.typ == EnumToken.AtRuleNodeType && !("chi" in node)) { // str = `${(node).val === "" ? "" : options.indent || " "}${(node).val};`; // } else { - str = renderAstNode(node, options, sourcemap, sourceLocation, linesMap, errors, reducer, cache, level + 1, indents); - } - if (css === "") { - return str; + if (sourcemaps != null) { + if (previousStr.length > 0) { + move(sourceLocation, linesMap, previousStr); + } + } + str = renderAstNode(node, options, sourcemaps, sourceLocation, linesMap, errors, reducer, cache, level + 1, indents); + previousStr = ""; } if (str === "") { - return css; + continue; } - return `${css}${options.newLine}${indentSub}${str}`; - }, ""); - if (options.removeEmpty && children === "") { - return ""; + str = options.newLine + indentSub + str; + children += str; + } + if (sourcemaps != null && str !== "") { + move(sourceLocation, linesMap, str.endsWith(";") ? str.slice(0, -1) : str); } if (children.endsWith(";")) { children = children.slice(0, -1); } - const rendered = [EnumToken.AtRuleNodeType, EnumToken.KeyframesAtRuleNodeType].includes(data.typ) - ? `@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val}${options.indent}{${options.newLine}` + - (children === "" ? "" : indentSub + children + options.newLine) + - indent + - `}` - : data.sel + - `${options.indent}{${options.newLine}` + - (children === "" ? "" : indentSub + children + options.newLine) + - indent + - `}`; - if (sourcemap != null && data[LOC] != null) { - updateSourceMap(data, options, cache, sourcemap, { ...sourceLocation }, linesMap.clone(), rendered); + if (options.removeEmpty && children === "") { + return ""; + } + const end = options.newLine + indent + `}`; + if (sourcemaps != null) { + move(sourceLocation, linesMap, end); } - return rendered; + return prelude + children + end; // case EnumToken.CssVariableTokenType: // case EnumToken.CssVariableImportTokenType: // return `@value ${(data).val}:${options.indent}${filterValues( @@ -316,6 +386,9 @@ function renderAstNode(data, options, sourcemap, sourceLocation, linesMap, error * render ast token * @param token * @param options + * @param cache + * @param reducer + * @param errors * @private */ function renderValue(token, options = {}, cache = Object.create(null), reducer, errors) { diff --git a/dist/lib/renderer/sourcemap/lib/codec.js b/dist/lib/renderer/sourcemap/lib/codec.js new file mode 100644 index 00000000..ab152589 --- /dev/null +++ b/dist/lib/renderer/sourcemap/lib/codec.js @@ -0,0 +1,78 @@ +// from https://github.com/Rich-Harris/vlq/tree/master +// credit: Rich Harris +const integer_to_char = {}; +const char_to_integer = {}; +let i = 0; +for (const char of 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=') { + char_to_integer[char] = i; + integer_to_char[i++] = char; +} +/** + * @param {string} str + */ +function decode(str) { + /** @type {number[]} */ + let result = []; + let shift = 0; + let value = 0; + for (let i = 0; i < str.length; i += 1) { + let integer = char_to_integer[str[i]]; + if (integer === undefined) { + throw new Error('Invalid character (' + str[i] + ')'); + } + const has_continuation_bit = integer & 32; + integer &= 31; + value += integer << shift; + if (has_continuation_bit) { + shift += 5; + } + else { + const should_negate = value & 1; + value >>>= 1; + if (should_negate) { + result.push(value === 0 ? -2147483648 : -value); + } + else { + result.push(value); + } + // reset + value = shift = 0; + } + } + return result; +} +/** + * + * @param value + * @returns + */ +function encode(value) { + if (typeof value === 'number') { + return encode_integer(value); + } + let result = ''; + for (let i = 0; i < value.length; i += 1) { + result += encode_integer(value[i]); + } + return result; +} +function encode_integer(num) { + let result = ''; + if (num < 0) { + num = (-num << 1) | 1; + } + else { + num <<= 1; + } + do { + let clamped = num & 31; + num >>>= 5; + if (num > 0) { + clamped |= 32; + } + result += integer_to_char[clamped]; + } while (num > 0); + return result; +} + +export { decode, encode }; diff --git a/dist/lib/renderer/sourcemap/lib/encode.js b/dist/lib/renderer/sourcemap/lib/encode.js deleted file mode 100644 index 9484f762..00000000 --- a/dist/lib/renderer/sourcemap/lib/encode.js +++ /dev/null @@ -1,37 +0,0 @@ -// from https://github.com/Rich-Harris/vlq/tree/master -// credit: Rich Harris -const integer_to_char = {}; -let i = 0; -for (const char of 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=') { - integer_to_char[i++] = char; -} -function encode(value) { - if (typeof value === 'number') { - return encode_integer(value); - } - let result = ''; - for (let i = 0; i < value.length; i += 1) { - result += encode_integer(value[i]); - } - return result; -} -function encode_integer(num) { - let result = ''; - if (num < 0) { - num = (-num << 1) | 1; - } - else { - num <<= 1; - } - do { - let clamped = num & 31; - num >>>= 5; - if (num > 0) { - clamped |= 32; - } - result += integer_to_char[clamped]; - } while (num > 0); - return result; -} - -export { encode }; diff --git a/dist/lib/renderer/sourcemap/sourcemap.js b/dist/lib/renderer/sourcemap/sourcemap.js index 8fb6c4ad..0099b322 100644 --- a/dist/lib/renderer/sourcemap/sourcemap.js +++ b/dist/lib/renderer/sourcemap/sourcemap.js @@ -1,10 +1,14 @@ -import { encode } from './lib/encode.js'; +import { decode, encode } from './lib/codec.js'; /** - * Source map class - * @internal + * Generate and parse source map */ class SourceMap { + /** + * + * @private + */ + keys = new Set(); /** * Last location */ @@ -19,6 +23,11 @@ class SourceMap { * @private */ sourcesMap = []; + /** + * Sources content + * @private + */ + sourcesContent = []; /** * Sources * @private @@ -27,52 +36,165 @@ class SourceMap { /** * Map * @private + * */ map = new Map(); + /** + * Map + * @private + * + */ + reverseMap = new Map(); /** * Line * @private */ line = -1; /** - * Add a location - * @param source - * @param original + * + * @param sourcemaps */ - add(newLine, newColumn, srcId, ln, col, sourceFileName, sourceContent) { - if (!this.sourcesMap.includes(srcId)) { - if (sourceFileName == null && sourceContent != null) { - sourceFileName = "data:text/css;charset=utf-8;base64," + btoa(sourceContent); + constructor(sourcemaps) { + if (typeof sourcemaps === "string") { + sourcemaps = JSON.parse(sourcemaps); + } + if (sourcemaps != null) { + this.sources = sourcemaps.sources?.slice() ?? []; + this.sourcesContent = sourcemaps.sourcesContent?.slice() ?? []; + const decodedMappings = sourcemaps.mappings + .split(";") + .map((mapping) => mapping.split(",").map((mapping) => decode(mapping))); + this.line = decodedMappings.length - 1; + for (let index = 0; index < decodedMappings.length; index++) { + if (decodedMappings[index].length == 0 || + (decodedMappings[index].length == 1 && decodedMappings[index][0].length == 0)) { + continue; + } + this.map.set(index, decodedMappings[index]); } - this.sourcesMap.push(srcId); - this.sources.push(sourceFileName || null); + this.computePositions(); } - const line = newLine - 1; - let record; - if (line > this.line) { - this.line = line; + } + /** + * Add all location + * @param maps + */ + addAll(maps) { + for (let [newLine, newColumn, srcId, ln, col, sourceFileName, sourceContent] of maps) { + const key = `${srcId}:${ln}:${sourceFileName}:${col}:${newLine}:${newColumn}:${sourceContent}`; + const sourcemap = `${srcId}:${sourceFileName}:${sourceContent}`; + if (this.keys.has(key)) { + continue; + } + this.keys.add(key); + if (!this.sourcesMap.includes(sourcemap)) { + this.sourcesMap.push(sourcemap); + this.sources.push(sourceFileName || null); + this.sourcesContent.push((sourceFileName != null ? null : sourceContent) || null); + } + const line = newLine - 1; + let record; + if (line > this.line) { + this.line = line; + } + if (!this.map.has(line)) { + record = [Math.max(0, newColumn - 1), this.sourcesMap.indexOf(sourcemap), ln - 1, col - 1]; + this.map.set(line, [record]); + } + else { + const arr = this.map.get(line); + record = [ + Math.max(0, newColumn - 1) - arr[0][0], + this.sourcesMap.indexOf(sourcemap) - arr[0][1], + ln - 1, + col - 1, + ]; + arr.push(record); + } + if (this.lastLocation != null) { + record[2] -= this.lastLocation.ln - 1; + record[3] -= this.lastLocation.col - 1; + } + this.lastLocation ??= { ln, col }; + this.lastLocation.ln = ln; + this.lastLocation.col = col; } - if (!this.map.has(line)) { - record = [Math.max(0, newColumn - 1), this.sourcesMap.indexOf(srcId), ln - 1, col - 1]; - this.map.set(line, [record]); + } + /** + * compute original positions + */ + computePositions() { + this.reverseMap.clear(); + let sourceFileIndex = 0; // second field + let sourceCodeLine = 0; // third field + let sourceCodeColumn = 0; // fourth field + let nameIndex = 0; // fifth field + let generatedCodeColumn; + let result; + // mappings to original source + for (let [i, line] of this.map.entries()) { + if (line.length === 0 || (line.length === 1 && line[0].length === 0)) { + continue; + } + generatedCodeColumn = line[0][0]; // first field - reset each time + line = line + .map((segment, index, array) => { + if (segment.length === 0) { + return []; + } + generatedCodeColumn = index == 0 ? segment[0] : segment[0] + array[0][0]; + result = [generatedCodeColumn]; + if (segment.length <= 1) { + return result; + } + sourceFileIndex = index == 0 ? segment[1] : segment[1] + array[0][1]; + sourceCodeLine += segment[2]; + sourceCodeColumn += segment[3]; + result.push(sourceFileIndex, sourceCodeLine, sourceCodeColumn); + if (segment.length === 5) { + nameIndex += segment[4]; + result.push(nameIndex); + } + return result; + }) + .sort((a, b) => { + if (a[1] !== b[1]) { + return a[1] - b[1]; + } + return a[0] - b[0]; + }); + if (line.length == 0 || (line.length == 1 && line[0].length == 0)) { + continue; + } + this.reverseMap.set(i, line); } - else { - const arr = this.map.get(line); - record = [ - Math.max(0, newColumn - 1 - arr[0][0]), - this.sourcesMap.indexOf(srcId) - arr[0][1], - ln - 1, - col - 1, - ]; - arr.push(record); + } + /** + * retrieve original sources, lines and columns + * @param line generated line + * @param column generated column + */ + find(line, column) { + if (!this.reverseMap.has(--line)) { + return null; } - if (this.lastLocation != null) { - record[2] -= this.lastLocation.ln - 1; - record[3] -= this.lastLocation.col - 1; + column--; + const result = []; + for (const record of this.reverseMap.get(line)) { + if (record.length == 0 || record[0] < column) { + continue; + } + if (record[0] > column) { + break; + } + result.push([ + this.sources?.[record[1]] ?? null, + record[2] + 1, + record[3] + 1, + this.sourcesContent?.[record[1]] ?? null, + ]); } - this.lastLocation ??= { ln, col }; - this.lastLocation.ln = ln; - this.lastLocation.col = col; + return result.length == 0 ? null : result; } /** * Convert to URL encoded string @@ -98,9 +220,16 @@ class SourceMap { return { version: this.version, sources: this.sources.slice(), + sourcesContent: this.sourcesContent?.slice(), mappings: mappings.join(";"), }; } + /** + * to string + */ + toString() { + return JSON.stringify(this); + } } export { SourceMap }; diff --git a/dist/lib/validation/match.js b/dist/lib/validation/match.js index 34ca59ac..dbed9b43 100644 --- a/dist/lib/validation/match.js +++ b/dist/lib/validation/match.js @@ -971,7 +971,7 @@ function matchSyntax(syntaxes, context, options) { if (syntaxes[i].isList) { result = matchListSyntax(syntaxes[i], context.slice(), options); if (result.success) { - options.visited.get(token).delete(syntaxes[i]); + options.visited.get(token)?.delete?.(syntaxes[i]); if (result.context.done()) { context.end(); return { diff --git a/dist/node.js b/dist/node.js index 21d0e975..ca818d15 100644 --- a/dist/node.js +++ b/dist/node.js @@ -14,6 +14,7 @@ import { ResponseType } from './types.js'; import { resolve as resolve$1 } from 'node:path'; import { SourceFile } from './lib/parser/source.js'; import { cwd } from 'node:process'; +import { parseResult } from './utils.js'; export { minify } from './lib/ast/minify.js'; export { expand } from './lib/ast/expand.js'; export { WalkerEvent, WalkerOptionEnum, walk, walkValues } from './lib/ast/walk.js'; @@ -163,7 +164,7 @@ function parseSync(...args) { } options ??= {}; options.src ??= ""; - options.sourcesMap ??= new Map; + options.sourcesMap ??= new Map(); Object.assign(options, { resolve, dirname, @@ -186,13 +187,10 @@ function parseSync(...args) { currentPosition: -1, }; const result = doParseSync(tokenize(options.parseInfo), options); - const { revMapping, ...res } = result; - return res; + return !options.module && !options.inputSourceMap ? result : parseResult(result, options); } /** * Transform css - * @param css - * @param options * * ```ts * @@ -203,6 +201,7 @@ function parseSync(...args) { * console.log(result.code); * ``` * + * @param args */ function transformSync(...args) { let options; @@ -314,7 +313,7 @@ async function parse(...args) { } options ??= {}; options.src ??= ""; - options.sourcesMap ??= new Map; + options.sourcesMap ??= new Map(); Object.assign(options, { load, resolve, @@ -337,10 +336,7 @@ async function parse(...args) { position: 0, currentPosition: -1, }; - return doParse(stream instanceof ReadableStream ? tokenizeStream(stream, options.parseInfo) : tokenize(options.parseInfo), options).then((result) => { - const { revMapping, ...res } = result; - return res; - }); + return doParse(stream instanceof ReadableStream ? tokenizeStream(stream, options.parseInfo) : tokenize(options.parseInfo), options).then((result) => (!options.module && !options.inputSourceMap ? result : parseResult(result, options))); } /** * Transform css file @@ -373,8 +369,6 @@ const transformFile = deprecate(async (file, options = {}, asStream = false) => }), "transformFile is deprecated, use transform instead as transform({file, asStream, ...options})"); /** * Transform css - * @param css - * @param options * * Parsing a string * @@ -413,6 +407,7 @@ const transformFile = deprecate(async (file, options = {}, asStream = false) => * * console.log(result.code); * ``` + * @param args */ async function transform(...args) { let options; diff --git a/dist/utils.d.ts b/dist/utils.d.ts new file mode 100644 index 00000000..17fbc10f --- /dev/null +++ b/dist/utils.d.ts @@ -0,0 +1,9 @@ +import type { ParseResult, ParserOptions } from "./@types/index.d.ts"; +/** + * parse result. process input sourcemap + * @param result + * @param options + * @returns + * @private + */ +export declare function parseResult(result: ParseResult, options: ParserOptions): ParseResult; diff --git a/dist/utils.js b/dist/utils.js new file mode 100644 index 00000000..e95f0443 --- /dev/null +++ b/dist/utils.js @@ -0,0 +1,49 @@ +import { EnumToken } from './lib/ast/types.js'; + +/** + * parse result. process input sourcemap + * @param result + * @param options + * @returns + * @private + */ +function parseResult(result, options) { + if (options.sourcemap != null && options.source.getInputSourceMap() == null) { + if (options.inputSourceMap != null) { + options.source.setInputSourceMap(options.inputSourceMap); + } + else { + // extract inline source map from the input CSS + const token = result.ast.chi.at(-1); + if (token?.typ == EnumToken.CommentTokenType && + token.val.startsWith("/*# sourceMappingURL=")) { + const data = token.val.slice(21, -2).trim(); + let sourcemap; + let encoding = ""; + if (data.startsWith("data:")) { + let offset = data.indexOf(",") + 1; + if (offset == 0) { + offset = data.lastIndexOf(";") + 1; + } + else { + encoding = data.slice(data.lastIndexOf(";") + 1, offset - 1); + } + if (encoding == "base64") { + sourcemap = atob(data.slice(offset)); + } + else { + sourcemap = decodeURIComponent(data.slice(offset)); + } + options.source.setInputSourceMap(sourcemap); + } + } + } + } + if (options.module) { + const { revMapping, ...res } = result; + return res; + } + return result; +} + +export { parseResult }; diff --git a/dist/web.js b/dist/web.js index 63d9f568..f0ce3778 100644 --- a/dist/web.js +++ b/dist/web.js @@ -8,6 +8,7 @@ import { tokenizeStream, tokenize } from './lib/parser/tokenize.js'; import { matchUrl, resolve, dirname } from './lib/fs/resolve.js'; import { ResponseType } from './types.js'; import { SourceFile } from './lib/parser/source.js'; +import { parseResult } from './utils.js'; export { minify } from './lib/ast/minify.js'; export { expand } from './lib/ast/expand.js'; export { WalkerEvent, WalkerOptionEnum, walk, walkValues } from './lib/ast/walk.js'; @@ -155,7 +156,7 @@ function parseSync(...args) { } options ??= {}; options.src ??= ""; - options.sourcesMap ??= new Map; + options.sourcesMap ??= new Map(); Object.assign(options, { resolve, dirname, @@ -180,13 +181,10 @@ function parseSync(...args) { currentPosition: -1, }; const result = doParseSync(tokenize(options.parseInfo), options); - const { revMapping, ...res } = result; - return res; + return !options.module && !options.inputSourceMap ? result : parseResult(result, options); } /** * Transform css - * @param css - * @param options * * ```ts * @@ -197,6 +195,7 @@ function parseSync(...args) { * console.log(result.code); * ``` * + * @param args */ function transformSync(...args) { let options; @@ -246,8 +245,6 @@ function transformSync(...args) { } /** * Parse css - * @param stream - * @param options * * Example: * @@ -271,6 +268,7 @@ function transformSync(...args) { * * console.log(result.ast); * ``` + * @param args */ async function parse(...args) { let options; @@ -292,7 +290,7 @@ async function parse(...args) { } options ??= {}; options.src ??= ""; - options.sourcesMap ??= new Map; + options.sourcesMap ??= new Map(); Object.assign(options, { load, resolve, @@ -316,10 +314,7 @@ async function parse(...args) { position: 0, currentPosition: -1, }; - return doParse(stream instanceof ReadableStream ? tokenizeStream(stream, options.parseInfo) : tokenize(options.parseInfo), options).then((result) => { - const { revMapping, ...res } = result; - return res; - }); + return doParse(stream instanceof ReadableStream ? tokenizeStream(stream, options.parseInfo) : tokenize(options.parseInfo), options).then((result) => (!options.module && !options.inputSourceMap ? result : parseResult(result, options))); } /** * Transform css file @@ -353,8 +348,6 @@ async function transformFile(file, options = {}, asStream = false) { } /** * Transform css - * @param css - * @param options * * Example: * @@ -372,6 +365,7 @@ async function transformFile(file, options = {}, asStream = false) { * * console.log(result.code); * ``` + * @param args */ async function transform(...args) { let options; diff --git a/files/getting-started.md b/files/getting-started.md index 13e6c9a5..a1702631 100644 --- a/files/getting-started.md +++ b/files/getting-started.md @@ -6,9 +6,9 @@ category: Guides ## About -CSS-Parser is a high-performance, fault-tolerant, and dependency-free CSS toolkit for Node.js and browsers. +CSS-Parser is a high-performance, fault-tolerant, and dependency-free all-in-one CSS parsing solution for Node.js and browsers. -It implements the [CSS Syntax Module Level 3](https://www.w3.org/TR/css-syntax-3/) specification and validates CSS using syntax rules from [MDN Data](https://github.com/mdn/data). +It implements the [CSS Syntax Module Level 3](https://www.w3.org/TR/css-syntax-3/) specification and validates CSS using syntax rules from [MDN Data](https://github.com/mdn/data). Every stylesheet is fully parsed into a structured AST, and token values are exposed as typed data so the library can support robust transformations, validation, and plugin-oriented workflows without falling back to raw strings. In addition to parsing and validation, CSS-Parser provides advanced optimization and minification capabilities. According to [this benchmark](https://tbela99.github.io/css-parser/benchmark/index.html), it is the most efficient CSS minifier available, producing smaller output than competing solutions while maintaining competitive performance. @@ -22,9 +22,11 @@ A non-exhaustive list of features is provided below: * **Zero dependencies** — lightweight and easy to integrate into any project. +* **All-in-one CSS parsing solution** for parsing, validation, transformation, and minification. * **Standards-based CSS validation** powered by MDN data. * **Full CSS Modules support** for modern component-based workflows. -* **Fault-tolerant parsing** that follows the CSS Syntax Module Level 3 specification. +* **Fault-tolerant parsing** that follows the CSS Syntax Module Level 3 specification and always yields a complete parse tree. +* **Typed tokens and AST** — parsed CSS is exposed as strongly typed tokens and nodes for safer plugin and transform logic. * **High-performance minification** with safe optimizations and no unsafe transforms. * **Advanced color processing** with support for modern color spaces and functions, including `color()`, `lab()`, `lch()`, `oklab()`, `oklch()`, `color-mix()`, `light-dark()`, system colors, and relative colors. * **Color conversion engine** capable of transforming colors between all supported formats. diff --git a/files/transform.md b/files/transform.md index 236970b3..3545bc28 100644 --- a/files/transform.md +++ b/files/transform.md @@ -8,6 +8,30 @@ category: Guides Visitors are used to transform the ast tree produced by the parser. For more information about the visitor object see the [typescript definition](../docs/interfaces/node.VisitorNodeMap.html) +## Plugin support through the visitor API + +The CSS parser supports plugin-style extensions through its visitor API. You can register handlers for specific AST node types and lifecycle events such as enter, visit, and leave to inspect, validate, or modify nodes without altering the parser internals. + +This pattern is useful for building reusable plugins that enforce conventions, inject transformations, or add custom analysis on top of the parsed AST. + +```ts +import {transform, type ParserOptions} from '@tbela99/css-parser'; + +const options: ParserOptions = { + visitor: { + Rule: { + '.card': (node) => { + node.selector = '.card, .panel'; + return node; + } + } + } +}; + +const result = await transform('.card { color: red; }', options); +console.log(result.code); +``` + ## Visitors execution order Visitors can be called when the node is entered, visited or left. diff --git a/llms.txt b/llms.txt index 50cb7811..ddf08057 100644 --- a/llms.txt +++ b/llms.txt @@ -1,9 +1,10 @@ # css-parser ## Project overview -- This repository contains css-parser, a dependency-free CSS parser, transformer, minifier, and validator for Node.js and browsers. +- This repository contains css-parser, a dependency-free all-in-one CSS parsing solution for Node.js and browsers. - The library follows the CSS Syntax Module Level 3 specification and uses MDN syntax data for validation. -- It is designed for fault-tolerant parsing, AST manipulation, CSS Modules, minification, color processing, and syntax lowering. +- It is designed for fault-tolerant parsing, full AST and typed-token analysis, AST manipulation, CSS Modules, minification, color processing, and syntax lowering. +- CSS is always fully parsed into a structured AST, and token values are typed so plugins and transforms can work against semantic data instead of raw strings. ## Installation - npm: npm install @tbela99/css-parser @@ -26,9 +27,15 @@ - Support CSS Modules with scoped class generation. - Minify CSS safely with options such as inlineCssVariables, computeCalcExpression, removeDuplicateDeclarations, and beautify. - Transform ASTs through visitors and custom traversal logic. +- Support plugin-style extensions through the visitor API, where custom handlers can observe and mutate AST nodes during enter/visit/leave phases. - Lower modern CSS syntax such as nested CSS and if() to broadly compatible output. - Generate source maps and handle advanced color functions and conversions. +## Visitor-based plugin model +- The parser exposes a visitor option that accepts node-specific handlers keyed by AST node type and event type. +- Plugins can be implemented as reusable visitor maps that inspect or transform nodes such as Rule, AtRule, Declaration, KeyframesRule, and Value nodes. +- This allows extension code to run alongside the core parser without modifying the parser itself. + ## Typical usage ```ts import {transform, ColorType} from '@tbela99/css-parser'; diff --git a/src/@types/index.d.ts b/src/@types/index.d.ts index fde50dcf..d32373ff 100644 --- a/src/@types/index.d.ts +++ b/src/@types/index.d.ts @@ -442,21 +442,38 @@ export declare interface ParseInputStreamOptions { input: string | ReadableStream; } +/** + * Input options for string or stream + * @internal + */ export declare interface ParseSourceOptions { sourcesMap?: Map; source?: SourceFile | null; } +export declare interface ParserSourceMapOptions { + /** + * Include sourcemap in the ast. Sourcemap info is always generated + */ + sourcemap?: boolean | "inline"; + /** + * Input source map + */ + inputSourceMap?: SourceMapObject | string; +} + export declare interface ParserSyncOptions - extends MinifyOptions, MinifyFeatureOptions, ValidationOptions, PropertyListOptions, ParseSourceOptions { + extends + MinifyOptions, + ParserSourceMapOptions, + MinifyFeatureOptions, + ValidationOptions, + PropertyListOptions, + ParseSourceOptions { /** * Source file to be used for sourcemap */ src?: string; - /** - * Include sourcemap in the ast. Sourcemap info is always generated - */ - sourcemap?: boolean | "inline"; /** * Remove at-rule charset */ @@ -658,6 +675,11 @@ export declare interface ResolvedPath { * Ast node render options */ export declare interface RenderOptions { + /** + * Source file to be used as CSS input file for sourcemap resolution + */ + src?: string; + /** * Minify css values. */ diff --git a/src/lib/ast/expand.ts b/src/lib/ast/expand.ts index 42a9f6ec..d975052d 100644 --- a/src/lib/ast/expand.ts +++ b/src/lib/ast/expand.ts @@ -1,10 +1,10 @@ -import { splitRule } from "./minify.ts"; -import { combinators, RAW } from "../syntax/constants.ts"; -import { parseString } from "../parser/parse.ts"; -import { walkValues } from "./walk.ts"; -import { renderValue } from "../renderer/render.ts"; -import type { AstAtRule, AstNode, AstRule, AstStyleSheet, LiteralToken, Token } from "../../@types/index.d.ts"; -import { EnumToken } from "./types.ts"; +import {splitRule} from "./minify.ts"; +import {combinators, RAW} from "../syntax/constants.ts"; +import {parseString} from "../parser/parse.ts"; +import {walkValues} from "./walk.ts"; +import {renderValue} from "../renderer/render.ts"; +import type {AstAtRule, AstNode, AstRule, AstStyleSheet, LiteralToken, Token} from "../../@types/index.d.ts"; +import {EnumToken} from "./types.ts"; /** * expand css nesting ast nodes @@ -70,9 +70,10 @@ function expandRule(node: AstRule): Array { continue; } - selRule.forEach((arr) => - combinators.includes(arr[0].charAt(0)) ? arr.unshift(arSelf) : arr.unshift(arSelf, " "), - ); + for (let i1 = 0; i1 < selRule.length; i1++) { + const arr = selRule[i1]; + combinators.includes(arr[0].charAt(0)) ? arr.unshift(arSelf) : arr.unshift(arSelf, " "); + } rule.sel = selRule .reduce( diff --git a/src/lib/ast/find.ts b/src/lib/ast/find.ts index 16e55e27..89647f2a 100644 --- a/src/lib/ast/find.ts +++ b/src/lib/ast/find.ts @@ -1,11 +1,11 @@ -import type { Token } from "../../@types/token.d.ts"; -import type { AstDeclaration, AstNode, AstValueMatcher, TokenSearchResult } from "../../@types/ast.d.ts"; -import { EnumToken } from "./types.ts"; -import { walk, walkValues } from "./walk.ts"; -import { PARENT, TOKENS } from "../syntax/constants.ts"; +import type {Token} from "../../@types/token.d.ts"; +import type {AstDeclaration, AstNode, AstValueMatcher, TokenSearchResult} from "../../@types/ast.d.ts"; +import {EnumToken} from "./types.ts"; +import {walk, walkValues} from "./walk.ts"; +import {PARENT, TOKENS} from "../syntax/constants.ts"; /** - * Search the ast sub-tree and return the first match + * Search the ast subtree and return the first match * * ```ts * // find the first ast declaration node which name is 'aspect-ratio' diff --git a/src/lib/ast/minify.ts b/src/lib/ast/minify.ts index 26c93106..5498a5b0 100644 --- a/src/lib/ast/minify.ts +++ b/src/lib/ast/minify.ts @@ -1,7 +1,7 @@ -import { eq } from "../parser/utils/eq.ts"; -import { doRender, renderValue } from "../renderer/render.ts"; +import {eq} from "../parser/utils/eq.ts"; +import {doRender, renderValue} from "../renderer/render.ts"; import * as allFeatures from "./features/index.ts"; -import { walkValues } from "./walk.ts"; +import {walkValues} from "./walk.ts"; import type { AstAtRule, AstDeclaration, @@ -24,15 +24,15 @@ import type { RawSelectorTokens, Token, } from "../../@types/index.d.ts"; -import { EnumToken } from "./types.ts"; -import { isFunction, isIdent, isIdentStart, isWhiteSpace } from "../syntax/syntax.ts"; -import { FeatureWalkMode } from "./features/type.ts"; -import { trimArray } from "../validation/match.ts"; -import { combinators, LOC, OPTIMIZED, PARENT, RAW, TOKENS } from "../syntax/constants.ts"; -import { replaceNodeOrValue } from "../parser/utils/token.ts"; -import { parseString } from "../parser/parse.ts"; -import { tokenize } from "../parser/tokenize.ts"; -import { replaceCompound } from "./expand.ts"; +import {EnumToken} from "./types.ts"; +import {isFunction, isIdent, isIdentStart, isWhiteSpace} from "../syntax/syntax.ts"; +import {FeatureWalkMode} from "./features/type.ts"; +import {trimArray} from "../validation/match.ts"; +import {combinators, LOC, OPTIMIZED, PARENT, RAW, TOKENS} from "../syntax/constants.ts"; +import {replaceNodeOrValue} from "../parser/utils/token.ts"; +import {parseString} from "../parser/parse.ts"; +import {tokenize} from "../parser/tokenize.ts"; +import {replaceCompound} from "./expand.ts"; const notEndingWith: string[] = ["(", "["].concat(combinators); const rules: EnumToken[] = [ @@ -72,11 +72,12 @@ export function minify( * @param errors * @param nestingContent * + * @param context * @private */ export function minify( ast: AstNode, - options: ParserOptions | MinifyFeatureOptions = {}, + opt: ParserOptions | MinifyFeatureOptions = {}, recursive: boolean = false, errors?: ErrorDescription[], nestingContent?: boolean, @@ -89,6 +90,9 @@ export function minify( let parents: Set; let replacement: AstNode | null; + // @ts-ignore + let {sourcemap, module, ...options} = opt; + if (!("features" in options)) { // @ts-ignore options = { @@ -357,9 +361,9 @@ function transformAtRuleMediaPrelude(values: Token[]) { * Minify at-rule media * - remove redundant tokens * - generate range queries - * @param ast * * @private + * @param tokens */ function minifyAtRuleMedia(tokens: Token[]): Token[] { let hasUpdates: boolean = false; @@ -496,7 +500,6 @@ function doMinify( while (previous?.typ === EnumToken.CommentNodeType) { previous = ast.chi[--nodeIndex]; - continue; } node = ast.chi![i] as AstNode; @@ -1107,7 +1110,9 @@ export function optimizeSelector(selector: string[][]): OptimizedSelector | null break; } - selector.forEach((selector: string[]) => selector.splice(0, optimized.length)); + for (let i1 = 0; i1 < selector.length; i1++) { + selector[i1].splice(0, optimized.length); + } let reducible: boolean = optimized.length == 1; @@ -1591,7 +1596,6 @@ function wrapNodes( * Diff nodes * @param n1 * @param n2 - * @param reducer * @param options * * @private @@ -1744,20 +1748,48 @@ function diff(n1: AstRule, n2: AstRule, options: ParserOptions = {}) { chi: intersect.reverse(), }; + let op = {level: 0, ...options}; + if ( result == null || [n1, n2].reduce((acc: number, curr: AstRule): number => { let css: string = options.cache!.get(curr) as string; if (css == null) { - css = doRender(curr, options).code; + let level: number = 0; + let parent: AstNode | null = curr[PARENT]; + + while (parent != null && parent.typ != EnumToken.StyleSheetNodeType) { + level++; + parent = parent[PARENT] as AstRule; + } + + op.level = level; + + css = doRender(curr, op).code; options.cache!.set(curr, css); } return curr.chi.length == 0 ? acc : acc + css.length; }, 0) <= [node1, node2, result].reduce((acc: number, curr: AstRule): number => { - const css = doRender(curr, options).code; + + let css: string = options.cache!.get(curr) as string; + + if (css != null) { + return curr.chi.length == 0 ? acc : acc + css.length + } + + let level: number = 0; + let parent: AstNode | null = curr[PARENT]; + + while (parent != null && parent.typ != EnumToken.StyleSheetNodeType) { + level++; + parent = parent[PARENT] as AstRule; + } + + op.level = level; + css = doRender(curr, op).code; return curr.chi.length == 0 ? acc : acc + css.length; }, 0) diff --git a/src/lib/fs/resolve.ts b/src/lib/fs/resolve.ts index 08428858..057e0874 100644 --- a/src/lib/fs/resolve.ts +++ b/src/lib/fs/resolve.ts @@ -1,5 +1,8 @@ import { memoize } from "../parser/utils/cache.ts"; +/** + * match url + */ export const matchUrl: RegExp = /^(https?:)?\/\//; /** @@ -13,6 +16,10 @@ export function dirname(path: string): string { return ""; } + if (path.startsWith("data:")) { + return path; + } + let i: number = 0; let parts: string[] = [""]; @@ -42,11 +49,7 @@ function splitPath(result: string): { i: number; parts: string[] } { return { parts: [], i: 0 }; } - // if (result === "/") { - // return { parts: ["/"], i: 0 }; - // } - - const parts: string[] = [""]; + const parts: string[] = result == "/" ? [] : [""]; let i: number = 0; for (; i < result.length; i++) { @@ -54,10 +57,10 @@ function splitPath(result: string): { i: number; parts: string[] } { if (chr == "/") { parts.push(""); - } + } // else if (chr == "?" || chr == "#") { // break; - // } + // } else { parts[parts.length - 1] += chr; } @@ -79,6 +82,8 @@ function splitPath(result: string): { i: number; parts: string[] } { /** * Nomalize path + * @param path + * @private */ export const normalize = memoize(function (path: string) { let parts: string[] = []; @@ -111,8 +116,8 @@ export const normalize = memoize(function (path: string) { while (++k < parts.length) { // if (parts[k] == ".") { // parts.splice(k--, 1); - // } else - if (parts[k] == "..") { + // } else + if (k > 0 && parts[k] == "..") { parts.splice(k - 1, 2); k -= 2; } @@ -121,9 +126,16 @@ export const normalize = memoize(function (path: string) { return (path.charAt(0) == "/" ? "/" : "") + parts.join("/"); }); +/** + * diff path + * @param path1 + * @param path2 + * @private + */ export const diff = memoize(function (path1: string, path2: string) { let { parts } = splitPath(path1); const { parts: dirs } = splitPath(path2); + for (const p of dirs) { if (parts[0] == p) { parts.shift(); @@ -148,7 +160,6 @@ export const resolve = memoize(function ( currentDirectory: string, cwd?: string, ): { absolute: string; relative: string } { - if (matchUrl.test(url)) { return { absolute: url, @@ -160,39 +171,57 @@ export const resolve = memoize(function ( currentDirectory ??= ""; url = normalize(url); - + + if (cwd !== "") { + cwd = normalize(cwd); + } if (currentDirectory !== "") { currentDirectory = normalize(currentDirectory); - - if (url.startsWith(currentDirectory + "/")) { - return { - absolute: url, - relative: url.slice(currentDirectory.length + 1), - }; - } } - if ((currentDirectory === "" || currentDirectory === ".") && cwd !== "") { - cwd = normalize(cwd); + const dir = cwd || currentDirectory; + const absolute = dir == "" || url.startsWith("/") ? resolvePath(url) : resolvePath(dir, url); - if (url.startsWith(cwd == "/" ? cwd : cwd + "/")) { - const absolute: string = url; - const prefix: string = cwd == "/" ? cwd : cwd + "/"; + return { + absolute, + relative: dir === "" ? absolute : diff(absolute, dir), + }; +}) as (url: string, currentDirectory?: string, cwd?: string) => { absolute: string; relative: string }; - return { - absolute, - relative: absolute.startsWith(prefix) ? absolute.slice(prefix.length) : diff(absolute, cwd), - }; +/** + * + * @param parts + * @returns + * @private + */ +function resolvePath(...parts: string[]): string { + const path = parts.filter(Boolean).join("/"); + const isAbsolute: boolean = /^[\\/]/.test(path); + const segments: string[] = path.split(/[\\/]+/); + const resolved: string[] = []; + + for (const segment of segments) { + if (!segment || segment === ".") { + continue; + } + + if (segment === "..") { + if (resolved.length && resolved[resolved.length - 1] !== "..") { + resolved.pop(); + } else if (!isAbsolute) { + resolved.push(".."); + } + } else { + resolved.push(segment); } } - return { - absolute: url, - relative: url === "" ? "" : diff(url, cwd || currentDirectory), - }; -}) as ( - url: string, - currentDirectory?: string, - cwd?: string, -) => { absolute: string; relative: string }; + let result = resolved.join("/"); + + if (isAbsolute) { + result = "/" + result; + } + + return result || (isAbsolute ? "/" : "."); +} diff --git a/src/lib/parser/linesmap.ts b/src/lib/parser/linesmap.ts index c59070b6..8f5fa34b 100644 --- a/src/lib/parser/linesmap.ts +++ b/src/lib/parser/linesmap.ts @@ -11,7 +11,7 @@ export class LineMap { * Constructor * @param lines */ - constructor(lines: number[]) { + constructor(lines: number[] = []) { if (lines.length === 0) { lines.push(0); } @@ -32,9 +32,8 @@ export class LineMap { } const column: number = offset - this.lineStarts[line]; - // [line, column] - return [line + 1, column === 0 ? 1 : column]; + return [line + 1, line === 0 ? column + 1 : column]; } /** diff --git a/src/lib/parser/parse.ts b/src/lib/parser/parse.ts index 2285cb55..6e85e4d9 100644 --- a/src/lib/parser/parse.ts +++ b/src/lib/parser/parse.ts @@ -29,23 +29,23 @@ import type { FunctionToken, GenericVisitorAstNodeHandlerMap, GenericVisitorHandler, + GenericVisitorResult, IdentToken, LoadResult, - SourceLocation, ModuleSyncOptions, ParseInfo, ParseResult, ParseResultStats, ParserOptions, + ParserSyncOptions, PseudoClassToken, ResolvedPath, + SourceLocation, StringToken, Token, TokenizeResult, UrlToken, WhitespaceToken, - GenericVisitorResult, - ParserSyncOptions, } from "../../@types/index.d.ts"; import { ERRORS, LOC, pageMarginBoxType, PARENT, ROOT, STATE, TOKENS, tokensfuncDefMap } from "../syntax/constants.ts"; import { hash, hashAlgorithms, syncHash } from "../parser/utils/hash.ts"; @@ -67,6 +67,7 @@ import { parseAtRuleFontFeatureValues } from "./utils/at-rule-font-feature-value import { matchGenericSyntax } from "./utils/at-rule-generic.ts"; import { memoize } from "./utils/cache.ts"; import { SourceFile } from "./source.ts"; +import { dirname } from "../fs/resolve.ts"; function renderTokens(tokens: Token[] | null | undefined, options?: any): string { if (tokens == null || tokens.length === 0) return ""; @@ -90,9 +91,7 @@ const BadTokensTypes: EnumToken[] = [ EnumToken.BadUrlTokenType, EnumToken.BadStringTokenType, ]; - -export const atRulesMap: Map = new Map([["keyframes", EnumToken.KeyframesAtRuleNodeType]]); - +new Map([["keyframes", EnumToken.KeyframesAtRuleNodeType]]); let keyNameCounter: number = 0; const forbiddenStartCharacters: number[] = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"].map((c) => c.charCodeAt(0), @@ -110,8 +109,6 @@ const forbiddenStartCharacters: number[] = ["0", "1", "2", "3", "4", "5", "6", " */ export const getShortNameGenerator = memoize( (localName: string, filePath: string, pattern: string, hashLength = 5): string => { - const key = `${localName}_${filePath}_${pattern}_${hashLength}`; - let value: string = keyNameCounter!.toString(36); keyNameCounter!++; @@ -517,8 +514,6 @@ export function doParseSync( Array | Record>>> >; - const imports: AstAtRule[] = []; - let item: TokenizeResult; let node: AstAtRule | AstRule | AstKeyFrameRule | AstKeyframesAtRule | AstDeclaration | AstComment | null; @@ -717,8 +712,6 @@ export function doParseSync( if ("chi" in node) { stack.push(node as AstAtRule | AstRule | AstKeyFrameRule); context = node as AstRuleList; - } else if (node.typ == EnumToken.AtRuleNodeType && (node as AstAtRule).nam === "import") { - imports.push(node); } } else if (item.token.typ == EnumToken.BlockStartTokenType) { let inBlock: number = 1; @@ -781,10 +774,6 @@ export function doParseSync( node = parseNode(tokens, context, options as ParserOptions, errors, stats, invalidNodes); if (node != null) { - if (node.typ == EnumToken.AtRuleNodeType && "import" === (node as AstAtRule).val) { - imports.push(node); - } - if ("chi" in node /* && node.typ != EnumToken.InvalidRuleNodeType */) { stack.push(node); context = node as AstRuleList; @@ -889,7 +878,7 @@ export function doParseSync( continue; } - if (replacement == null || replacement == node) { + if (replacement == node) { continue; } @@ -961,7 +950,7 @@ export function doParseSync( continue; } - if (replacement == null || replacement == node) { + if (replacement == node) { continue; } @@ -1006,7 +995,7 @@ export function doParseSync( continue; } - if (replacement != null && replacement != node) { + if (replacement != node) { node = replacement as AstNode; } } @@ -1048,7 +1037,7 @@ export function doParseSync( continue; } - if (result != null && result != node) { + if (result != node) { node = result as Token; } @@ -1273,7 +1262,7 @@ export function doParseSync( if (node.typ == EnumToken.DeclarationNodeType) { if (node.nam.startsWith("--")) { if (!(node.nam in namesMapping)) { - let result = + let value: string = moduleSettings.scoped! & ModuleScopeEnumOptions.Global ? node.nam : moduleSettings.generateScopedName!( @@ -1282,7 +1271,6 @@ export function doParseSync( moduleSettings.pattern as string, moduleSettings.hashLength, ); - let value: string = result; mapping[node.nam] = "--" + @@ -1336,7 +1324,7 @@ export function doParseSync( } if (!((rule as IdentToken).val in mapping)) { - let result = + let value: string = moduleSettings.scoped! & ModuleScopeEnumOptions.Global ? (rule as IdentToken).val : moduleSettings.generateScopedName!( @@ -1345,7 +1333,6 @@ export function doParseSync( moduleSettings.pattern as string, moduleSettings.hashLength, ); - let value: string = result; mapping[(rule as DashedIdentToken | IdentToken).val] = (rule.typ == EnumToken.DashedIdenTokenType ? "--" : "") + @@ -1552,7 +1539,7 @@ export function doParseSync( ].includes((value as IdentToken).val) ) { if (!((value as IdentToken).val in mapping)) { - const result = + mapping[(value as IdentToken).val] = moduleSettings.scoped! & ModuleScopeEnumOptions.Global ? (value as IdentToken).val : moduleSettings.generateScopedName!( @@ -1561,7 +1548,6 @@ export function doParseSync( moduleSettings.pattern as string, moduleSettings.hashLength, ); - mapping[(value as IdentToken).val] = result; revMapping[mapping[(value as IdentToken).val]] = (value as IdentToken).val; } @@ -1658,7 +1644,7 @@ export function doParseSync( const val: string = (value as ClassSelectorToken).val.slice(1); if (!(val in mapping)) { - const result = + let value: string = moduleSettings.scoped! & ModuleScopeEnumOptions.Global ? val : moduleSettings.generateScopedName!( @@ -1667,7 +1653,6 @@ export function doParseSync( moduleSettings.pattern as string, moduleSettings.hashLength, ); - let value: string = result; mapping[val] = moduleSettings.naming! & ModuleCaseTransformEnum.DashCaseOnly || @@ -1711,7 +1696,7 @@ export function doParseSync( (prefix == "" && value.typ == EnumToken.IdenTokenType) ) { if (!((value as DashedIdentToken | IdentToken).val in mapping)) { - const result = + let val: string = moduleSettings.scoped! & ModuleScopeEnumOptions.Global ? (value as DashedIdentToken | IdentToken).val : moduleSettings.generateScopedName!( @@ -1720,7 +1705,6 @@ export function doParseSync( moduleSettings.pattern as string, moduleSettings.hashLength, ); - let val: string = result; mapping[(value as DashedIdentToken | IdentToken).val] = prefix + @@ -2036,6 +2020,7 @@ export async function doParse( : // @ts-expect-error ((iter as Iterator).next().value as TokenizeResult)) ) { + stats.bytesIn = item.bytesIn; stats.tokensCount++; @@ -2171,7 +2156,7 @@ export async function doParse( const url: string = token.typ == EnumToken.StringTokenType ? token.val.slice(1, -1) : token.val; try { - const src = options.resolve!(url, options.src || (options.cwd as string)) as ResolvedPath; + const src = options.resolve!(url, options.src ? dirname(options.src as string) : (options.cwd as string)) as ResolvedPath; const result = options.load!(src) as LoadResult; const stream = result instanceof Promise || Object.getPrototypeOf(result).constructor.name == "AsyncFunction" @@ -2798,6 +2783,8 @@ export async function doParse( continue; } + const resolvedSrc = options.resolve!(options.src as string, options.cwd as string); + for (const token of composeSelectors) { // composes: a b c; if (token.r == null) { @@ -2887,9 +2874,11 @@ export async function doParse( }) as ParserOptions, ); - const srcIndex: string = - (src.relative.startsWith("/") || src.relative.startsWith("../") ? "" : "./") + - src.relative; + let srcIndex: string = options.resolve!(src.absolute, resolvedSrc.absolute).relative; + + if (!srcIndex.startsWith("/") && !srcIndex.startsWith("../")) { + srcIndex = `./${srcIndex}`; + } if (Object.keys(root.mapping as Record).length > 0) { importMapping[srcIndex] = {} as Record; @@ -3216,28 +3205,6 @@ export async function doParse( ) { (parent as AstRule)[TOKENS]!.splice(index, 1); } - - // if (val == ":global") { - // for (; index < (parent as AstRule)[TOKENS]!.length; index++) { - // if ( - // (parent as AstRule)[TOKENS]![index].typ == - // EnumToken.CommaTokenType || - // ([ - // EnumToken.PseudoClassFuncTokenType, - // EnumToken.PseudoClassTokenType, - // ].includes((parent as AstRule)[TOKENS]![index].typ) && - // [":global", ":local"].includes( - // ( - // (parent as AstRule)[TOKENS]![index] as PseudoClassToken - // ).val.toLowerCase(), - // )) - // ) { - // break; - // } - - // global.add((parent as AstRule)[TOKENS]![index]); - // } - // } } break; @@ -3257,13 +3224,6 @@ export async function doParse( ); break; - // (parent as AstRule)[TOKENS]!.splice( - // (parent as AstRule)[TOKENS]!.indexOf(value), - // 1, - // ...(value as FunctionToken).chi, - // ); - - // break; } } }, @@ -3652,6 +3612,8 @@ function parseNode( } /** + * @param stream + * @param context * @param options * @param errors * @param parseAsBlock @@ -3750,7 +3712,6 @@ export function parseAtRule( } if (syntax != null && atRule.nam !== "layer" && parseAsBlock !== blockAllowed) { - success = false; errors.push({ action: "drop", node: atRule, @@ -4333,7 +4294,7 @@ export function parseAtRule( action: "drop", node: atRule, location: options.source!.getSourceLocation(atRule[LOC]!.sta), - message: "node is allowd only in @page rule", + message: "node is allowed only in @page rule", }); } else { trimArray(stream); @@ -4498,9 +4459,6 @@ export function parseAtRule( if (result.errors.length > 0) { errors.push(...result.errors); } - // else if (atRuleName === "document") { - // parseUrlToken(stream); - // } if (result.success) { let i: number = 0; diff --git a/src/lib/parser/source.ts b/src/lib/parser/source.ts index 9ef29a73..a65ee98d 100644 --- a/src/lib/parser/source.ts +++ b/src/lib/parser/source.ts @@ -1,5 +1,7 @@ +import type { SourceMapObject } from "../../@types/index.d.ts"; +import { SourceMap } from "../renderer/sourcemap/sourcemap.ts"; import { LineMap } from "./linesmap.ts"; -import type {SourceLocation} from "../../@types/ast.d.ts"; + /** * Source file ID */ @@ -9,6 +11,7 @@ let sourceId: number = 0; * Source file helper class */ export class SourceFile { + private inputSourceMap: SourceMap | null = null; /** * Source file ID @@ -29,10 +32,9 @@ export class SourceFile { /** * Constructor - * @param id - * @param content - * @param lines - * @param file + * @param content + * @param lines + * @param file */ constructor(content: string, lines: number[], file: string | null = null) { this.id = sourceId++; @@ -43,8 +45,7 @@ export class SourceFile { /** * Update source content - * @param content - * @param lines + * @param content */ append(content: string) { this.content += content; @@ -52,16 +53,15 @@ export class SourceFile { /** * get file name - * @returns + * @returns */ getFileName(): string | null { - return this.file; } /** * get content - * @returns + * @returns */ getContent(): string { return this.content; @@ -69,9 +69,9 @@ export class SourceFile { /** * get text - * @param start - * @param length - * @returns + * @param start + * @param length + * @returns */ getText(start: number, length: number): string { return this.content.slice(start, start + length); @@ -79,8 +79,8 @@ export class SourceFile { /** * Compute line and column of the offset - * @param offset - * @returns + * @param offset + * @returns */ getOffsets(offset: number): [number, number] { return this.lineStarts.getOffsets(offset); @@ -88,26 +88,42 @@ export class SourceFile { /** * get source location - * @param offset - * @returns + * @param offset + * @returns */ getSourceLocation(offset: number): [string | null, number, number] { - return [this.file, ... this.getOffsets(offset)]; + return [this.file, ...this.getOffsets(offset)]; } /** * get line starts - * @returns + * @returns */ getLineStarts(): number[] { return this.lineStarts.getLineStarts(); } - + /** * add line start - * @param lineStart + * @param lineStart */ addLineStart(lineStart: number) { this.lineStarts.addLineStart(lineStart); } + + /** + * set input source map + * @param inputSourceMap + */ + setInputSourceMap(inputSourceMap: SourceMapObject | string | null) { + this.inputSourceMap = inputSourceMap == null ? null : new SourceMap(inputSourceMap as SourceMapObject | string); + } + + /** + * return input source map + * @returns + */ + getInputSourceMap(): SourceMap | null { + return this.inputSourceMap; + } } diff --git a/src/lib/parser/tokenize.ts b/src/lib/parser/tokenize.ts index 42071314..865ecc9e 100644 --- a/src/lib/parser/tokenize.ts +++ b/src/lib/parser/tokenize.ts @@ -477,7 +477,7 @@ export function next(parseInfo: ParseInfo, count: number = 1): string { } /** - * Tokenize css string + * Tokenize CSS string * @param parseInfo * @param yieldEOFToken */ @@ -495,7 +495,6 @@ export function tokenize(parseInfo: ParseInfo | string, yieldEOFToken: boolean = } let value: string; - let nextValue: string; let buffer: string = parseInfo.buffer; let charCode: number; let nextCharCode: number; @@ -509,9 +508,6 @@ export function tokenize(parseInfo: ParseInfo | string, yieldEOFToken: boolean = while ((value = peek(parseInfo))) { charCode = value.charCodeAt(0); - // nextCharCode = nextValue.charCodeAt(0); - - // console.debug({value, buffer}); switch (charCode) { case TokenMap.EQUALS: @@ -962,7 +958,7 @@ export function tokenize(parseInfo: ParseInfo | string, yieldEOFToken: boolean = next(parseInfo); // EOF - if (!(nextValue = peek(parseInfo))) { + if (!(peek(parseInfo))) { // end of stream ignore \\ if (buffer.length > 0) { result.push(yieldResult(buffer, parseInfo)); @@ -973,11 +969,6 @@ export function tokenize(parseInfo: ParseInfo | string, yieldEOFToken: boolean = } buffer += value + next(parseInfo); - - // buffer += - // (parseInfo.offset == parseInfo.currentPosition - // ? parseInfo.buffer.slice(-1) - // : parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset - 1)) + value; break; case TokenMap.SINGLE_QUOTE: diff --git a/src/lib/renderer/render.ts b/src/lib/renderer/render.ts index 1c1387da..c87f1742 100644 --- a/src/lib/renderer/render.ts +++ b/src/lib/renderer/render.ts @@ -28,7 +28,6 @@ import type { LengthToken, ListToken, LiteralToken, - SourceLocation, MediaFeatureToken, MediaQueryConditionToken, MediaQueryUnaryFeatureToken, @@ -39,6 +38,7 @@ import type { PseudoPageToken, RenderOptions, RenderResult, + SourceLocation, StringToken, SupportsQueryConditionToken, SupportsQueryUnaryConditionToken, @@ -47,19 +47,18 @@ import type { WhenElseUnaryConditionToken, WrappedValuesToken, } from "../../@types/index.d.ts"; -import { convertColor } from "../syntax/color/color.ts"; -import { getAngle } from "../syntax/color/color.ts"; +import { convertColor, getAngle } from "../syntax/color/color.ts"; import { reduceHexValue } from "../syntax/color/hex.ts"; import { ColorType, EnumToken } from "../ast/types.ts"; import { expand } from "../ast/expand.ts"; import { SourceMap } from "./sourcemap/sourcemap.ts"; import { colorPrecision, LOC, PARENT, pseudoElements, tokensfuncSet, urlTokenMatcher } from "../syntax/constants.ts"; import { + minifyNumber, parseColor, - reducegradientBackgroundPosition, reduceColorStops, reduceConicColorStops, - minifyNumber, + reducegradientBackgroundPosition, toPrecisionAngle, toPrecisionValue, } from "../syntax/syntax.ts"; @@ -67,6 +66,7 @@ import { equalsIgnoreCase } from "../parser/utils/text.ts"; import { toDegrees } from "../parser/utils/angle.ts"; import { LineMap as LinesMap } from "../parser/linesmap.ts"; import { dirname } from "../fs/resolve.ts"; +import { SourceFile } from "../parser/source.ts"; /** * render ast @@ -133,6 +133,8 @@ export function doRender( const startTime: number = performance.now(); const errors: ErrorDescription[] = []; const sourcemap: SourceMap | null = options.sourcemap ? new SourceMap() : null; + const sourcemaps: Array<[number, number, number, number, number, string | null, string | null]> | null = + options.sourcemap ? [] : null; const cache: { [key: string]: any; } = Object.create(null); @@ -142,13 +144,24 @@ export function doRender( sta: 0, end: 0, } as SourceLocation; - const linesMap = new LinesMap([]); + const linesMap: LinesMap | null = options.sourcemap ? new LinesMap() : null; let code: string = ""; if (mapping != null) { if (mapping.importMapping != null) { - for (const [key, value] of Object.entries(mapping.importMapping)) { + const absolutePath = options.resolve!( + options.output != null ? dirname(options.output as string) : dirname(options.src as string), + options.cwd as string, + ).absolute; + + for (let [key, value] of Object.entries(mapping.importMapping)) { + key = options.resolve!(options.resolve!(key, options.cwd as string).absolute, absolutePath).relative; + + if (!key.startsWith("/") && !key.startsWith(".")) { + key = "./" + key; + } + code += `:import("${key}")${options.indent}{${options.newLine}${Object.entries(value).reduce( (acc, [k, v]) => acc + (acc.length > 0 ? options.newLine : "") + `${options.indent}${v}:${options.indent}${k};`, @@ -162,7 +175,10 @@ export function doRender( acc + (acc.length > 0 ? options.newLine : "") + `${options.indent}${k}:${options.indent}${v};`, "", )}${options.newLine}}${options.newLine}`; - move(sourceLocation, linesMap, code); + + if (sourcemap != null) { + move(sourceLocation, linesMap!, code); + } } if (options.output != null) { @@ -182,7 +198,7 @@ export function doRender( ? expand(data as AstStyleSheet | AstAtRule | AstRule) : data, options, - sourcemap, + sourcemaps, sourceLocation, linesMap, errors, @@ -206,6 +222,7 @@ export function doRender( }; if (sourcemap != null) { + sourcemap.addAll(sourcemaps!); result.map = sourcemap; if (options.sourcemap === "inline") { @@ -221,8 +238,9 @@ export function doRender( * @param node * @param options * @param cache - * @param sourcemap - * @param position + * @param sourcemaps + * @param sourceLocation + * @param linesMap * @param str * * @internal @@ -233,12 +251,33 @@ function updateSourceMap( cache: { [p: string]: any; }, - sourcemap: SourceMap, + sourcemaps: Array<[number, number, number, number, number, string | null, string | null]>, sourceLocation: SourceLocation, linesMap: LinesMap, str: string, ) { + let offset: number = 0; + + while (true) { + if (str.charAt(offset) == options.newLine) { + offset += options.newLine.length; + continue; + } + + if (str.charAt(offset) == options.indent) { + offset += options.indent.length; + continue; + } + + break; + } + + if (offset > 0) { + move(sourceLocation, linesMap, str.slice(0, offset)); + } + if ( + node[LOC] != null && [ EnumToken.RuleNodeType, EnumToken.AtRuleNodeType, @@ -246,35 +285,71 @@ function updateSourceMap( EnumToken.KeyframesAtRuleNodeType, ].includes(node.typ) ) { - let srcId: number = (node[LOC])?.srcId ?? 0; + const source = options.sourcesMap!.get((node[LOC] as SourceLocation)!.srcId) as SourceFile; + const inputSourceMap = source.getInputSourceMap(); + const offsets: [number, number] = source.getOffsets(node[LOC].sta) as [number, number]; + const [newLine, newColumn] = linesMap.getOffsets(sourceLocation.end); + let records: Array<[string | null, number, number, string | null]> | null = null; + let srcId: number = (node[LOC] as SourceLocation)!.srcId; + let sourceFileName: string | null = (source.getFileName() as string) || null; + let sourceContent: string | null = (source.getContent() as string) || null; + + if (inputSourceMap != null && (records = inputSourceMap.find(offsets[0], offsets[1])) != null) { + for (const record of records) { + // @ts-ignore + sourceFileName = (record[0] as string) || null; + // @ts-ignore + offsets[0] = record[1] as number; + // @ts-ignore + offsets[1] = record[2] as number; + + sourceContent = (record[3] as string) || null; + + if (sourceFileName != null && options.output != null && !sourceFileName.startsWith("data:")) { + if (cache[sourceFileName] == null) { + const absolute = options.resolve!(dirname(options.output as string), options.cwd as string) + .absolute as string; + const absoluteSourcePath = options.resolve!( + dirname(options.src! || ("" as string)), + options.cwd as string, + ).absolute; + // resolution is relative to the source file + const absoluteSourceFileName = options.resolve!(sourceFileName, absoluteSourcePath as string) + .absolute as string; + + cache[sourceFileName] = options.resolve!(absoluteSourceFileName, absolute).relative as string; + } + + sourceFileName = cache[sourceFileName] as string; + } - let sourceFileName: string | null = (options.sourcesMap?.get(srcId)?.getFileName?.() as string) || null; + sourcemaps.push([newLine, newColumn, srcId, ...offsets, sourceFileName as string, sourceContent]); + } + } else { + if (sourceFileName != null && options.output != null && !sourceFileName.startsWith("data:")) { + if (cache[sourceFileName] == null) { + const absolute = options.resolve!(dirname(options.output as string), options.cwd as string) + .absolute as string; + const absoluteSourceFileName = options.resolve!(sourceFileName, options.cwd as string) + .absolute as string; + + cache[sourceFileName] = options.resolve!(absoluteSourceFileName, absolute).relative as string; + } - if (sourceFileName != null && options.output != null) { - if (cache[sourceFileName] == null) { - cache[sourceFileName] = options.resolve!(sourceFileName, dirname(options.output)).relative as string; + sourceFileName = cache[sourceFileName] as string; } - sourceFileName = cache[sourceFileName] as string; + sourcemaps.push([newLine, newColumn, srcId, ...offsets, sourceFileName as string, sourceContent]); } - - // @ts-ignore - sourcemap.add( - ...linesMap.getOffsets(sourceLocation.end), - srcId, - // @ts-ignore - ...(options.sourcesMap?.get(srcId)?.getOffsets(sourceLocation.sta) as [number, number]), - sourceFileName as string, - options.sourcesMap?.get(srcId)?.getContent?.() as string, - ); } - move(sourceLocation, linesMap, str); + move(sourceLocation, linesMap, offset > 0 ? str.slice(offset) : str); } /** * Update position - * @param position + * @param sourceLocation + * @param linesMap * @param str */ export function move(sourceLocation: SourceLocation, linesMap: LinesMap, str: string) { @@ -310,8 +385,9 @@ export function move(sourceLocation: SourceLocation, linesMap: LinesMap, str: st * render ast node * @param data * @param options - * @param sourcemap - * @param position + * @param sourcemaps + * @param sourceLocation + * @param linesMap * @param errors * @param reducer * @param cache @@ -323,9 +399,9 @@ export function move(sourceLocation: SourceLocation, linesMap: LinesMap, str: st function renderAstNode( data: AstNode, options: RenderOptions, - sourcemap: SourceMap | null, + sourcemaps: Array<[number, number, number, number, number, string | null, string | null]> | null, sourceLocation: SourceLocation, - linesMap: LinesMap, + linesMap: LinesMap | null, errors: ErrorDescription[], reducer: (acc: string, curr: Token) => string, cache: { @@ -342,6 +418,11 @@ function renderAstNode( indents.push((options.indent).repeat(level + 1)); } + // @ts-ignore + let children: string = ""; + let str: string = ""; + let previousStr: string = ""; + const indent: string = indents[level]; const indentSub: string = indents[level + 1]; @@ -364,13 +445,11 @@ function renderAstNode( : ""; case EnumToken.StyleSheetNodeType: - return (data).chi.reduce((css: string, node: AstRuleList | AstComment) => { - const hasPreviousContent = css !== ""; - - const str: string = renderAstNode( + for (const node of (data).chi) { + str = renderAstNode( node, options, - sourcemap, + sourcemaps, sourceLocation, linesMap, errors, @@ -381,27 +460,17 @@ function renderAstNode( ); if (str === "") { - return css; + continue; } - if (sourcemap != null && node[LOC] != null) { - updateSourceMap( - node, - options, - cache, - sourcemap, - sourceLocation, - linesMap, - (hasPreviousContent ? options.newLine : "") + str, - ); + if (children.length > 0) { + str = options.newLine + str; } - if (!hasPreviousContent) { - return str; - } + children += str; + } - return `${css}${options.newLine}${str}`; - }, ""); + return children; case EnumToken.AtRuleNodeType: case EnumToken.RuleNodeType: @@ -413,10 +482,20 @@ function renderAstNode( };`; } - // @ts-ignore - let children: string = (data).chi.reduce((css: string, node: AstNode) => { - let str: string; + const prelude = [EnumToken.AtRuleNodeType, EnumToken.KeyframesAtRuleNodeType].includes(data.typ) + ? `@${(data).nam}${(data).val === "" ? "" : options.indent || " "}${ + (data).val + }${options.indent}{` + : (data).sel + `${options.indent}{`; + + if (sourcemaps != null) { + updateSourceMap(data, options, cache, sourcemaps, sourceLocation, linesMap!, prelude); + } + let node: AstNode; + let k: number = (data as AstRule | AstAtRule).chi!.length - 1; + for (let i = 0; i < (data as AstRule | AstAtRule).chi!.length; i++) { + node = (data as AstRule | AstAtRule).chi![i]; if (node.typ == EnumToken.CommentNodeType) { str = options.removeComments && @@ -440,15 +519,29 @@ function renderAstNode( ) .reduce(reducer, "") .trimEnd()};`; + + if (sourcemaps != null) { + if (previousStr.length > 0) { + move(sourceLocation, linesMap!, previousStr); + } + } + + previousStr = str === "" ? "" : options.newLine + indentSub + str; } // else if (node.typ == EnumToken.AtRuleNodeType && !("chi" in node)) { // str = `${(node).val === "" ? "" : options.indent || " "}${(node).val};`; // } else { + if (sourcemaps != null) { + if (previousStr.length > 0) { + move(sourceLocation, linesMap!, previousStr); + } + } + str = renderAstNode( node, options, - sourcemap, + sourcemaps, sourceLocation, linesMap, errors, @@ -457,53 +550,36 @@ function renderAstNode( level + 1, indents, ); - } - if (css === "") { - return str; + previousStr = ""; } if (str === "") { - return css; + continue; } - return `${css}${options.newLine}${indentSub}${str}`; - }, ""); + str = options.newLine + indentSub + str; + children += str; + } - if (options.removeEmpty && children === "") { - return ""; + if (sourcemaps != null && str !== "") { + move(sourceLocation, linesMap!, str.endsWith(";") ? str.slice(0, -1) : str); } if (children.endsWith(";")) { children = children.slice(0, -1); } + if (options.removeEmpty && children === "") { + return ""; + } - const rendered = [EnumToken.AtRuleNodeType, EnumToken.KeyframesAtRuleNodeType].includes(data.typ) - ? `@${(data).nam}${(data).val === "" ? "" : options.indent || " "}${ - (data).val - }${options.indent}{${options.newLine}` + - (children === "" ? "" : indentSub + children + options.newLine) + - indent + - `}` - : (data).sel + - `${options.indent}{${options.newLine}` + - (children === "" ? "" : indentSub + children + options.newLine) + - indent + - `}`; - - if (sourcemap != null && data[LOC] != null) { - updateSourceMap( - data as AstRuleList, - options, - cache, - sourcemap, - { ...sourceLocation }, - linesMap.clone(), - rendered, - ); + const end: string = options.newLine + indent + `}`; + + if (sourcemaps != null) { + move(sourceLocation, linesMap!, end); } - return rendered; + return prelude + children + end; // case EnumToken.CssVariableTokenType: // case EnumToken.CssVariableImportTokenType: @@ -535,6 +611,9 @@ function renderAstNode( * render ast token * @param token * @param options + * @param cache + * @param reducer + * @param errors * @private */ export function renderValue( diff --git a/src/lib/renderer/sourcemap/lib/encode.ts b/src/lib/renderer/sourcemap/lib/codec.ts similarity index 52% rename from src/lib/renderer/sourcemap/lib/encode.ts rename to src/lib/renderer/sourcemap/lib/codec.ts index ead4a1f1..fd45e13a 100644 --- a/src/lib/renderer/sourcemap/lib/encode.ts +++ b/src/lib/renderer/sourcemap/lib/codec.ts @@ -1,13 +1,61 @@ // from https://github.com/Rich-Harris/vlq/tree/master // credit: Rich Harris const integer_to_char: { [key: number]: string } = {}; - +const char_to_integer: { [key: string]: number } = {}; let i = 0; for (const char of 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=') { + char_to_integer[char] = i; integer_to_char[i++] = char; } +/** + * @param {string} str + */ +export function decode(str: string) { + /** @type {number[]} */ + let result = []; + + let shift = 0; + let value = 0; + + for (let i = 0; i < str.length; i += 1) { + let integer = char_to_integer[str[i]]; + + if (integer === undefined) { + throw new Error('Invalid character (' + str[i] + ')'); + } + + const has_continuation_bit = integer & 32; + + integer &= 31; + value += integer << shift; + + if (has_continuation_bit) { + shift += 5; + } else { + const should_negate = value & 1; + value >>>= 1; + + if (should_negate) { + result.push(value === 0 ? -0x80000000 : -value); + } else { + result.push(value); + } + + // reset + value = shift = 0; + } + } + + return result; +} + +/** + * + * @param value + * @returns + */ export function encode(value: number | number[]) { if (typeof value === 'number') { return encode_integer(value); diff --git a/src/lib/renderer/sourcemap/sourcemap.ts b/src/lib/renderer/sourcemap/sourcemap.ts index d846ccd5..a53747fc 100644 --- a/src/lib/renderer/sourcemap/sourcemap.ts +++ b/src/lib/renderer/sourcemap/sourcemap.ts @@ -1,11 +1,16 @@ import type { SourceMapObject } from "../../../@types/index.d.ts"; -import { encode } from "./lib/encode.ts"; +import { decode, encode } from "./lib/codec.ts"; /** - * Source map class - * @internal + * Generate and parse source map */ export class SourceMap { + /** + * + * @private + */ + private keys: Set = new Set(); + /** * Last location */ @@ -20,19 +25,34 @@ export class SourceMap { * Sources map * @private */ - private sourcesMap: number[] = []; + private sourcesMap: string[] = []; + + /** + * Sources content + * @private + */ + private readonly sourcesContent: Array = []; /** * Sources * @private */ - private sources: Array = []; + private readonly sources: Array = []; /** * Map * @private + * */ private map: Map = new Map(); + + /** + * Map + * @private + * + */ + private reverseMap: Map = new Map(); + /** * Line * @private @@ -40,60 +60,195 @@ export class SourceMap { private line: number = -1; /** - * Add a location - * @param source - * @param original - */ - add( - newLine: number, - newColumn: number, - srcId: number, - ln: number, - col: number, - sourceFileName: string, - sourceContent: string, - ) { - if (!this.sourcesMap.includes(srcId)) { - if (sourceFileName == null && sourceContent != null) { - sourceFileName = "data:text/css;charset=utf-8;base64," + btoa(sourceContent); + * + */ + constructor(); + /** + * + * @param sourcemaps + */ + constructor(sourcemaps: string | SourceMapObject); + /** + * + * @param sourcemaps + */ + constructor(sourcemaps?: SourceMapObject | string) { + if (typeof sourcemaps === "string") { + sourcemaps = JSON.parse(sourcemaps) as SourceMapObject; + } + + if (sourcemaps != null) { + this.sources = sourcemaps.sources?.slice() ?? []; + this.sourcesContent = sourcemaps.sourcesContent?.slice() ?? []; + const decodedMappings = sourcemaps.mappings + .split(";") + .map((mapping) => mapping.split(",").map((mapping) => decode(mapping))) as number[][][]; + + this.line = decodedMappings.length - 1; + + for (let index = 0; index < decodedMappings.length; index++) { + if ( + decodedMappings[index].length == 0 || + (decodedMappings[index].length == 1 && decodedMappings[index][0].length == 0) + ) { + continue; + } + + this.map.set(index, decodedMappings[index]); } - this.sourcesMap.push(srcId); - this.sources.push((sourceFileName as string) || null); + this.computePositions(); } + } + + /** + * Add all location + * @param maps + */ + addAll(maps: Array<[number, number, number, number, number, string | null, string | null]>): void { + for (let [newLine, newColumn, srcId, ln, col, sourceFileName, sourceContent] of maps) { + const key = `${srcId}:${ln}:${sourceFileName}:${col}:${newLine}:${newColumn}:${sourceContent}`; + const sourcemap = `${srcId}:${sourceFileName}:${sourceContent}`; + + if (this.keys.has(key)) { + continue; + } + + this.keys.add(key); + + if (!this.sourcesMap.includes(sourcemap)) { + this.sourcesMap.push(sourcemap); + this.sources.push((sourceFileName as string) || null); + this.sourcesContent.push((sourceFileName != null ? null : sourceContent) || null); + } + + const line: number = newLine - 1; + let record: number[]; + + if (line > this.line) { + this.line = line; + } + + if (!this.map.has(line)) { + record = [Math.max(0, newColumn - 1), this.sourcesMap.indexOf(sourcemap), ln - 1, col - 1]; - const line = newLine - 1; - let record: number[]; + this.map.set(line, [record]); + } else { + const arr: number[][] = this.map.get(line) as number[][]; + + record = [ + Math.max(0, newColumn - 1) - arr[0][0], + this.sourcesMap.indexOf(sourcemap) - arr[0][1], + ln - 1, + col - 1, + ]; + arr.push(record); + } - if (line > this.line) { - this.line = line; + if (this.lastLocation != null) { + record[2] -= this.lastLocation.ln - 1; + record[3] -= this.lastLocation.col - 1; + } + + this.lastLocation ??= { ln, col }; + this.lastLocation.ln = ln; + this.lastLocation.col = col; } + } + + /** + * compute original positions + */ + computePositions(): void { + this.reverseMap.clear(); + let sourceFileIndex: number = 0; // second field + let sourceCodeLine: number = 0; // third field + let sourceCodeColumn: number = 0; // fourth field + let nameIndex: number = 0; // fifth field + let generatedCodeColumn: number; + let result: number[]; + + // mappings to original source + for (let [i, line] of this.map.entries()) { + if (line.length === 0 || (line.length === 1 && line[0].length === 0)) { + continue; + } + + generatedCodeColumn = line[0][0]; // first field - reset each time + + line = line + .map((segment: number[], index: number, array: number[][]) => { + if (segment.length === 0) { + return []; + } + + generatedCodeColumn = index == 0 ? segment[0] : segment[0] + array[0][0]; + + result = [generatedCodeColumn]; + + if (segment.length <= 1) { + return result; + } - if (!this.map.has(line)) { - record = [Math.max(0, newColumn - 1), this.sourcesMap.indexOf(srcId), ln - 1, col - 1]; + sourceFileIndex = index == 0 ? segment[1] : segment[1] + array[0][1]; + sourceCodeLine += segment[2]; + sourceCodeColumn += segment[3]; - this.map.set(line, [record]); - } else { - const arr: number[][] = this.map.get(line); + result.push(sourceFileIndex, sourceCodeLine, sourceCodeColumn); - record = [ - Math.max(0, newColumn - 1 - arr[0][0]), - this.sourcesMap.indexOf(srcId) - arr[0][1], - ln - 1, - col - 1, - ]; - arr.push(record); + if (segment.length === 5) { + nameIndex += segment[4]; + result.push(nameIndex); + } + + return result; + }) + .sort((a, b) => { + if (a[1] !== b[1]) { + return a[1] - b[1]; + } + + return a[0] - b[0]; + }); + + if (line.length == 0 || (line.length == 1 && line[0].length == 0)) { + continue; + } + + this.reverseMap.set(i, line); } + } - if (this.lastLocation != null) { - record[2] -= this.lastLocation.ln - 1; - record[3] -= this.lastLocation.col - 1; + /** + * retrieve original sources, lines and columns + * @param line generated line + * @param column generated column + */ + find(line: number, column: number): Array<[string | null, number, number, string | null]> | null { + if (!this.reverseMap.has(--line)) { + return null; } - this.lastLocation ??= { ln, col }; + column--; + const result: Array<[string | null, number, number, string | null]> = []; - this.lastLocation.ln = ln; - this.lastLocation.col = col; + for (const record of this.reverseMap.get(line)!) { + if (record.length == 0 || record[0] < column) { + continue; + } + if (record[0] > column) { + break; + } + + result.push([ + this.sources?.[record[1]] ?? null, + record[2] + 1, + record[3] + 1, + this.sourcesContent?.[record[1]] ?? null, + ]); + } + + return result.length == 0 ? null : result; } /** @@ -128,7 +283,15 @@ export class SourceMap { return { version: this.version, sources: this.sources.slice(), + sourcesContent: this.sourcesContent?.slice(), mappings: mappings.join(";"), }; } + + /** + * to string + */ + toString(): string { + return JSON.stringify(this); + } } diff --git a/src/lib/validation/match.ts b/src/lib/validation/match.ts index a32f0795..0458949b 100644 --- a/src/lib/validation/match.ts +++ b/src/lib/validation/match.ts @@ -1323,7 +1323,7 @@ function matchSyntax( result = matchListSyntax(syntaxes[i], context.slice(), options); if (result.success) { - (options.visited!.get(token) as Set)!.delete(syntaxes[i]); + (options.visited!.get(token) as Set)?.delete?.(syntaxes[i]); if (result.context.done()) { context.end(); diff --git a/src/node.ts b/src/node.ts index b762c3bd..56ee1a39 100644 --- a/src/node.ts +++ b/src/node.ts @@ -1,4 +1,5 @@ import type { + AstComment, AstNode, LoadResult, ParseInfo, @@ -20,13 +21,14 @@ import { createReadStream } from "node:fs"; import { lstat, readFile } from "node:fs/promises"; import { doParse, doParseSync } from "./lib/parser/parse.ts"; import { doRender } from "./lib/renderer/render.ts"; -import { ModuleScopeEnumOptions } from "./lib/ast/types.ts"; +import { EnumToken, ModuleScopeEnumOptions } from "./lib/ast/types.ts"; import { tokenize, tokenizeStream } from "./lib/parser/tokenize.ts"; import { dirname, matchUrl, resolve } from "./lib/fs/resolve.ts"; import { ResponseType } from "./types.ts"; import { resolve as resolvePath } from "node:path"; import { SourceFile } from "./lib/parser/source.ts"; import { cwd } from "node:process"; +import { parseResult } from "./utils.ts"; export type * from "./@types/index.d.ts"; export type * from "./@types/ast.d.ts"; @@ -230,7 +232,6 @@ export function parseSync(stream: string, options?: ParserSyncOptions): ParseRes /** * Parse css string - * @param stream * @param options * * Parsing a string @@ -283,7 +284,7 @@ export function parseSync( options ??= {}; options.src ??= ""; - options.sourcesMap ??= new Map; + options.sourcesMap ??= new Map(); Object.assign(options, { resolve, @@ -310,10 +311,8 @@ export function parseSync( currentPosition: -1, } as ParseInfo; - const result = doParseSync(tokenize(options.parseInfo), options); - - const { revMapping, ...res } = result; - return res as ParseResult; + const result = doParseSync(tokenize(options.parseInfo), options) as ParseResult; + return !options.module && !options.inputSourceMap ? result : parseResult(result, options); } /** @@ -352,8 +351,6 @@ export function transformSync(options: ParseInputOptions & TransformSyncOptions) /** * Transform css - * @param css - * @param options * * ```ts * @@ -364,6 +361,7 @@ export function transformSync(options: ParseInputOptions & TransformSyncOptions) * console.log(result.code); * ``` * + * @param args */ export function transformSync( ...args: [string, TransformSyncOptions?] | [ParseInputOptions & TransformSyncOptions] @@ -476,7 +474,6 @@ export async function parse(stream: string | ReadableStream, options /** * Parse css - * @param stream * @param options * * @throws Error file not found @@ -511,7 +508,6 @@ export async function parse(options: ParseInputFileOptions & ParserOptions): Pro /** * Parse css - * @param stream * @param options * * Parsing a string @@ -629,7 +625,7 @@ export async function parse( options ??= {}; options.src ??= ""; - options.sourcesMap ??= new Map; + options.sourcesMap ??= new Map(); Object.assign(options, { load, @@ -641,7 +637,6 @@ export async function parse( options.src = resolve(options.src!, options.cwd).relative; if (options.source == null) { - const source = new SourceFile(typeof stream == "string" ? stream : "", [], options.src); options.sourcesMap.set(source.id, source); options.source = source; @@ -661,10 +656,7 @@ export async function parse( return doParse( stream instanceof ReadableStream ? tokenizeStream(stream, options.parseInfo) : tokenize(options.parseInfo), options, - ).then((result) => { - const { revMapping, ...res } = result; - return res as ParseResult; - }); + ).then((result) => (!options.module && !options.inputSourceMap ? result : parseResult(result, options))); } /** @@ -752,7 +744,6 @@ export async function transform( /** * Transform css - * @param css * @param options * * Parsing a string @@ -798,7 +789,6 @@ export async function transform(options: ParseInputStreamOptions & TransformOpti /** * Transform css - * @param css * @param options * * Parsing a string @@ -843,8 +833,6 @@ export async function transform(options: ParseInputFileOptions & TransformOption /** * Transform css - * @param css - * @param options * * Parsing a string * @@ -883,6 +871,7 @@ export async function transform(options: ParseInputFileOptions & TransformOption * * console.log(result.code); * ``` + * @param args */ export async function transform( ...args: diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 00000000..11387fff --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,54 @@ +import type { AstComment, ParseResult, ParserOptions } from "./@types/index.d.ts"; +import { EnumToken } from "./lib/ast/types.ts"; + +/** + * parse result. process input sourcemap + * @param result + * @param options + * @returns + * @private + */ +export function parseResult(result: ParseResult, options: ParserOptions): ParseResult { + if (options.sourcemap != null && options!.source!.getInputSourceMap() == null) { + if (options.inputSourceMap != null) { + options!.source!.setInputSourceMap(options.inputSourceMap); + } else { + // extract inline source map from the input CSS + const token = result.ast.chi.at(-1); + + if ( + token?.typ == EnumToken.CommentTokenType && + (token as AstComment).val.startsWith("/*# sourceMappingURL=") + ) { + const data = (token as AstComment).val.slice(21, -2).trim(); + let sourcemap: string; + let encoding: string = ""; + + if (data.startsWith("data:")) { + let offset: number = data.indexOf(",") + 1; + + if (offset == 0) { + offset = data.lastIndexOf(";") + 1; + } else { + encoding = data.slice(data.lastIndexOf(";") + 1, offset - 1); + } + + if (encoding == "base64") { + sourcemap = atob(data.slice(offset)); + } else { + sourcemap = decodeURIComponent(data.slice(offset)); + } + + options!.source!.setInputSourceMap(sourcemap); + } + } + } + } + + if (options.module) { + const { revMapping, ...res } = result; + return res as ParseResult; + } + + return result; +} diff --git a/src/web.ts b/src/web.ts index 4f961d59..11781e44 100644 --- a/src/web.ts +++ b/src/web.ts @@ -22,6 +22,7 @@ import { tokenize, tokenizeStream } from "./lib/parser/tokenize.ts"; import { dirname, matchUrl, resolve } from "./lib/fs/resolve.ts"; import { ResponseType } from "./types.ts"; import { SourceFile } from "./lib/parser/source.ts"; +import { parseResult } from "./utils.ts"; export type * from "./@types/index.d.ts"; export type * from "./@types/ast.d.ts"; @@ -220,7 +221,6 @@ export function parseSync(stream: string, options?: ParserSyncOptions): ParseRes /** * Parse css string - * @param stream * @param options * * Parsing a string @@ -273,7 +273,7 @@ export function parseSync( options ??= {}; options.src ??= ""; - options.sourcesMap ??= new Map; + options.sourcesMap ??= new Map(); Object.assign(options, { resolve, @@ -287,7 +287,7 @@ export function parseSync( options.src = resolve(options.src!, options.cwd).relative; if (options.source == null) { - const source = new SourceFile(typeof stream == "string" ? stream : "", [], options.src) + const source = new SourceFile(typeof stream == "string" ? stream : "", [], options.src); options.sourcesMap.set(source.id, source); options.source = source; } @@ -304,9 +304,7 @@ export function parseSync( } as ParseInfo; const result = doParseSync(tokenize(options.parseInfo), options); - - const { revMapping, ...res } = result; - return res as ParseResult; + return !options.module && !options.inputSourceMap ? result : parseResult(result, options); } /** @@ -345,8 +343,6 @@ export function transformSync(options: ParseInputOptions & TransformSyncOptions) /** * Transform css - * @param css - * @param options * * ```ts * @@ -357,6 +353,7 @@ export function transformSync(options: ParseInputOptions & TransformSyncOptions) * console.log(result.code); * ``` * + * @param args */ export function transformSync( ...args: [string, TransformSyncOptions?] | [ParseInputOptions & TransformSyncOptions] @@ -425,8 +422,6 @@ export async function parse(options: ParseInputStreamOptions & ParserOptions): P /** * Parse css - * @param stream - * @param options * * Example: * @@ -450,6 +445,7 @@ export async function parse(options: ParseInputStreamOptions & ParserOptions): P * * console.log(result.ast); * ``` + * @param args */ export async function parse( ...args: @@ -484,7 +480,7 @@ export async function parse( options ??= {}; options.src ??= ""; - options.sourcesMap ??= new Map; + options.sourcesMap ??= new Map(); Object.assign(options, { load, @@ -499,7 +495,7 @@ export async function parse( options.src = resolve(options.src!, options.cwd).relative; if (options.source == null) { - const source = new SourceFile(typeof stream === "string" ? stream : "", [], options.src) + const source = new SourceFile(typeof stream === "string" ? stream : "", [], options.src); options.sourcesMap.set(source.id, source); options.source = source; } @@ -517,10 +513,7 @@ export async function parse( return doParse( stream instanceof ReadableStream ? tokenizeStream(stream, options.parseInfo) : tokenize(options.parseInfo), options, - ).then((result) => { - const { revMapping, ...res } = result; - return res as ParseResult; - }); + ).then((result) => (!options.module && !options.inputSourceMap ? result : parseResult(result, options))); } /** @@ -571,8 +564,6 @@ export async function transform(options: ParseInputStreamOptions & TransformOpti /** * Transform css - * @param css - * @param options * * Example: * @@ -590,6 +581,7 @@ export async function transform(options: ParseInputStreamOptions & TransformOpti * * console.log(result.code); * ``` + * @param args */ export async function transform( ...args: diff --git a/test/specs/code/block.js b/test/specs/code/block.js index eb12a38c..5250c7bb 100644 --- a/test/specs/code/block.js +++ b/test/specs/code/block.js @@ -1134,10 +1134,14 @@ font-family: random-item(--x, {Times, serif}, {Arial, sans-serif}, {Courier, mon }); it("stream file #50", async () => { - const dir = resolve((import.meta.dirname ?? dirname(new URL(import.meta.url).pathname)) + "/../..").absolute; + // const dir = resolve((import.meta.dirname ?? dirname(new URL(import.meta.url).pathname)) + "/../..").absolute; // const file = `@import '${dir}/files/css/line-awesome.css`; + + const url = new URL(import.meta.url); + url.pathname = dirname(url.pathname) + "/../../files/css/bootstrap-4.css"; + const options = { - file: `${dir}/files/css/bootstrap-4.css`, + file: url.pathname , beautify: true, }; @@ -1149,10 +1153,12 @@ font-family: random-item(--x, {Times, serif}, {Arial, sans-serif}, {Courier, mon }); it("stream file #51", async () => { - const dir = resolve((import.meta.dirname ?? dirname(new URL(import.meta.url).pathname)) + "/../..").absolute; - // const file = `@import '${dir}/files/css/line-awesome.css`; + + const url = new URL(import.meta.url); + url.pathname = dirname(url.pathname) + "/../../files/css/tailwind.css"; + const options = { - file: `${dir}/files/css/tailwind.css`, + file: url.pathname, beautify: true, }; diff --git a/test/specs/code/import1.js b/test/specs/code/import1.js index 891f287e..485d7bd6 100644 --- a/test/specs/code/import1.js +++ b/test/specs/code/import1.js @@ -1,8 +1,9 @@ export function run(describe, expect, it, transform, parse, render, dirname) { + const url = new URL(dirname(import.meta.url) + '/../../files/css/color.css?v=1'); const atRule = ` -@import '${(import.meta.dirname ?? dirname(new URL(import.meta.url).pathname)).replace(/\\/g, '/') + '/../../files/css/color.css?v=1'}'; +@import '${url.pathname}'; abbr[title], abbr[data-original-title] { text-decoration: underline dotted; -webkit-text-decoration: underline dotted; diff --git a/test/specs/code/modules.js b/test/specs/code/modules.js index 3ca56f6b..f73093b5 100644 --- a/test/specs/code/modules.js +++ b/test/specs/code/modules.js @@ -99,6 +99,9 @@ export function run(describe, expect, it, transform, parse, render, dirname, rea }); it("module #4", function () { + + const url = new URL(dirname(import.meta.url) + '/../../css-modules/mixins.css'); + return transform( ` .goal .bg-indigo { @@ -107,7 +110,7 @@ export function run(describe, expect, it, transform, parse, render, dirname, rea .indigo-white { composes: bg-indigo; -composes: button cell title from "${(import.meta.dirname ?? dirname(new URL(import.meta.url).pathname)).replaceAll("\\", "/")}/../../css-modules/mixins.css"; color: white; +composes: button cell title from "${url.pathname}"; color: white; } `, { @@ -119,7 +122,7 @@ composes: button cell title from "${(import.meta.dirname ?? dirname(new URL(impo goal: "goal_r7bhp", "bg-indigo": "bg-indigo_gy28g", "indigo-white": - "indigo-white_wims0 bg-indigo_gy28g button_rptz7_mixins cell_dptz7_mixins title_fnrx5_mixins", + "indigo-white_wims0 bg-indigo_gy28g button_egkqy_mixins cell_s04ai_mixins title_seiow_mixins", }); expect(result.code).equals(`.goal_r7bhp .bg-indigo_gy28g { @@ -610,6 +613,9 @@ a span { }); it("module mode ICSS #17", function () { + + + const url = new URL(dirname(import.meta.url) + '/../../css-modules/mixins.css'); return transform( ` @@ -626,7 +632,7 @@ a span { .indigo-white { composes: bg-indigo; - composes: button cell title from "${(import.meta.dirname ?? dirname(new URL(import.meta.url).pathname)).replaceAll("\\", "/")}/../../css-modules/mixins.css"; color: white; + composes: button cell title from "${url.pathname}"; color: white; } `, { @@ -719,11 +725,13 @@ a span { // }); it("module import variables #19", function () { + + const url = new URL(dirname(import.meta.url) + '/../../css-modules/color.css'); return transform( ` /* import your colors... */ - @value colors: "${(import.meta.dirname ?? dirname(new URL(import.meta.url).pathname)).replaceAll("\\", "/")}/../../css-modules/color.css"; + @value colors: "${url.pathname}"; @value blue, red, green from colors; .button { diff --git a/test/specs/code/sourcemaps.js b/test/specs/code/sourcemaps.js index 9badb040..ec49c0ce 100644 --- a/test/specs/code/sourcemaps.js +++ b/test/specs/code/sourcemaps.js @@ -1,20 +1,44 @@ +import { ColorType, EnumToken, ModuleCaseTransformEnum, ModuleScopeEnumOptions } from "../../../dist/lib/ast/types.js"; + export function run(describe, expect, it, transform, parse, render, dirname, readFile, resolve) { - // describe('sourcemap', function () { + describe('sourcemap', function () { - // const dir = resolve((import.meta.dirname ?? dirname(new URL(import.meta.url).pathname)) + '/../..').absolute; - // // const file = `@import '${dir}/files/css/line-awesome.css`; - // const options = { - // file: `${dir}/files/css/line-awesome.css`, - // sourcemap: 'inline', - // }; + + const url = new URL(dirname(import.meta.url) + '/../../css-modules/mixins.css'); // const file = `@import '${dir}/files/css/line-awesome.css`; + const options = { + input: ` + + .goal .bg-indigo { + background: indigo; + } + + + .indigo-white { + composes: bg-indigo; + composes: title block ruler from global; + color: white; + } + + .indigo-white { + composes: bg-indigo; + composes: button cell title from "${url.pathname}"; color: white; + } + `, + beautify: true, + sourcemap: 'inline', + module: ModuleScopeEnumOptions.ICSS, + output: 'test/sourcemap.html' + }; - // it('sourcemap file #1', async () => { + it('sourcemap file #1', async () => { - // return transform(options).then(async result => { + return transform(options).then(async result => { - // return readFile(`${dir}/files/sourcemap/line-awesome-sourcemap.css`, {encoding: 'utf-8'}).then(expected => expect(`/*# sourceMappingURL=${result.map.toUrl()} */`).equals(expected.trim())); - // }); - // }); - // }); + result.map.computePositions(); + const positions = result.map.find(11, 1); + return expect(positions.length == 1 && positions[0].slice(0, 3)).deep.equals([null, 3, 15]) + }); + }); + }); } \ No newline at end of file diff --git a/test/specs/code/validation.js b/test/specs/code/validation.js index 13592a9c..8124a458 100644 --- a/test/specs/code/validation.js +++ b/test/specs/code/validation.js @@ -505,13 +505,15 @@ html, body, div, span, applet, object, iframe, it('file validation #21', function () { - return transform(`@import '${(import.meta.dirname ?? dirname(new URL(import.meta.url).pathname)).replaceAll('\\', '/')}/../../files/css/full.css'; + const url = new URL(dirname(import.meta.url) + '/../../files/css/full.css'); + return transform(`@import '${url.pathname}'; `, {validation: true, resolveImport: true}).then(result => expect(result.errors.length).equals(5)); }); it('file validation #22', function () { - transform(`@import '${(import.meta.dirname ?? dirname(new URL(import.meta.url).pathname)).replaceAll('\\', '/')}/../../files/css/bootstrap.css'; + const url = new URL(dirname(import.meta.url) + '/../../files/css/bootstrap.css'); + transform(`@import '${url.pathname}'; `, { validation: true, resolveImport: true @@ -520,7 +522,8 @@ html, body, div, span, applet, object, iframe, it('file validation #23', function () { - return transform(`@import '${(import.meta.dirname ?? dirname(new URL(import.meta.url).pathname)).replaceAll('\\', '/')}/../../files/css/bootstrap-4.css'; + const url = new URL(dirname(import.meta.url) + '/../../files/css/bootstrap-4.css'); + return transform(`@import '${url.pathname}'; `, { validation: true, resolveImport: true @@ -529,7 +532,8 @@ html, body, div, span, applet, object, iframe, it('file validation #24', function () { - return transform(`@import '${(import.meta.dirname ?? dirname(new URL(import.meta.url).pathname)).replaceAll('\\', '/')}/../../files/css/bootstrap-5.css'; + const url = new URL(dirname(import.meta.url) + '/../../files/css/bootstrap-5.css'); + return transform(`@import '${url.pathname}'; `, { validation: true, resolveImport: true @@ -538,7 +542,8 @@ html, body, div, span, applet, object, iframe, it('file validation #25', function () { - return transform(`@import '${(import.meta.dirname ?? dirname(new URL(import.meta.url).pathname)).replaceAll('\\', '/')}/../../files/css/tailwind.css'; + const url = new URL(dirname(import.meta.url) + '/../../files/css/tailwind.css'); + return transform(`@import '${url.pathname}'; `, { validation: true, resolveImport: true @@ -547,7 +552,9 @@ html, body, div, span, applet, object, iframe, it('file validation #26', function () { - return transform(`@import '${(import.meta.dirname ?? dirname(new URL(import.meta.url).pathname)).replaceAll('\\', '/')}/../../files/css/tailwind-2.0.4.css'; + const url = new URL(dirname(import.meta.url) + '/../../files/css/tailwind-2.0.4.css'); + + return transform(`@import '${url.pathname}'; `, { validation: true, resolveImport: true @@ -556,7 +563,9 @@ html, body, div, span, applet, object, iframe, it('file validation #27', function () { - return transform(`@import '${(import.meta.dirname ?? dirname(new URL(import.meta.url).pathname)).replaceAll('\\', '/')}/../../files/css/github-markdown.css'; + const url = new URL(dirname(import.meta.url) + '/../../files/css/github-markdown.css'); + + return transform(`@import '${url.pathname}'; `, { validation: true, resolveImport: true From aff9f3b914b3fe151915627d0e8dab24027292ef Mon Sep 17 00:00:00 2001 From: Thierry Bela Nanga Date: Sun, 16 Aug 2026 17:59:06 -0400 Subject: [PATCH 04/22] fix sourcemap bugs #146 --- .gitattributes | 2 + README.md | 1 + dist/index-umd-web.js | 118 ++++++++++++++++++++++------------ dist/index.cjs | 118 ++++++++++++++++++++++------------ dist/lib/ast/expand.js | 28 ++++++-- dist/lib/parser/linesmap.js | 4 +- dist/lib/parser/parse.js | 31 ++++----- dist/lib/renderer/render.js | 59 ++++++++++------- files/index.md | 2 + files/minification.md | 2 +- files/sourcemap.md | 111 ++++++++++++++++++++++++++++++++ files/syntax-lowering.md | 2 +- files/transform.md | 111 ++++++++++++++++---------------- files/usage.md | 29 --------- jsr.json | 2 +- package.json | 2 +- src/lib/ast/expand.ts | 49 ++++++++++---- src/lib/ast/minify.ts | 37 ++++++----- src/lib/parser/linesmap.ts | 4 +- src/lib/parser/parse.ts | 46 +++++++------ src/lib/renderer/render.ts | 77 +++++++++++++--------- test/specs/code/sourcemaps.js | 88 +++++++++++++++---------- 22 files changed, 589 insertions(+), 334 deletions(-) create mode 100644 files/sourcemap.md diff --git a/.gitattributes b/.gitattributes index 5fcf2092..12cf0de1 100644 --- a/.gitattributes +++ b/.gitattributes @@ -18,10 +18,12 @@ /.github/** linguist-vendored # exclude all files in test/ from stats /rollup.config.js linguist-vendored +/dist/** linguist-vendored /docs/** linguist-vendored /tools/** linguist-vendored /dist/** linguist-vendored /test/** linguist-vendored +/benchmark/** linguist-vendored /coverage/** linguist-vendored # # do not replace lf by crlf diff --git a/README.md b/README.md index 8833e01f..a9b6e6af 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,7 @@ Try it [online](https://tbela99.github.io/css-parser/playground/) - [CSS Modules](https://tbela99.github.io/css-parser/docs/documents/Guide.CSS_Modules.html) - [Minification](https://tbela99.github.io/css-parser/docs/documents/Guide.Minification.html) - [Custom Transform](https://tbela99.github.io/css-parser/docs/documents/Guide.Custom_Transform.html) +- [Sourcema](https://tbela99.github.io/css-parser/docs/documents/Guide.Sourcemap.html) - [Syntax Lowering](https://tbela99.github.io/css-parser/docs/documents/Guide.Syntax_Lowering.html) - [Ast Manipulation](https://tbela99.github.io/css-parser/docs/documents/Guide.Ast_Manipulation.html) - [Utility Functions](https://tbela99.github.io/css-parser/docs/documents/Guide.Utility_Functions.html) diff --git a/dist/index-umd-web.js b/dist/index-umd-web.js index c71e7413..7c67416e 100644 --- a/dist/index-umd-web.js +++ b/dist/index-umd-web.js @@ -21726,9 +21726,9 @@ if (offset < 0 || line < 0) { return [1, 1]; } - const column = offset - this.lineStarts[line]; + const column = offset - this.lineStarts[line] + 1; // [line, column] - return [line + 1, line === 0 ? column + 1 : column]; + return [line + 1, column == 0 ? 1 : column]; } /** * search the greatest index of the value less than or equal to offset @@ -24137,11 +24137,16 @@ */ function expand(ast) { const result = { ...ast, chi: [] }; + let children; for (let i = 0; i < ast.chi.length; i++) { - const node = ast.chi[i]; + let node = ast.chi[i]; if (node.typ === exports.EnumToken.RuleNodeType) { + children = expandRule(node); + for (const child of children) { + child[PARENT] = result; + } // @ts-ignore - result.chi.push(...expandRule(node)); + result.chi.push(...children); } else if (node.typ == exports.EnumToken.AtRuleNodeType && "chi" in node) { let hasRule = false; @@ -24153,10 +24158,23 @@ break; } } - // @ts-ignore - result.chi.push({ ...(hasRule ? expand(node) : node) }); + if (hasRule) { + node = expand(node); + for (const child of node.chi) { + child[PARENT] = result; + } + node[PARENT] = result; + // @ts-ignore + result.chi.push(node); + } + else { + node[PARENT] = result; + // @ts-ignore + result.chi.push(node); + } } else { + node[PARENT] = result; // @ts-ignore result.chi.push(node); } @@ -24809,7 +24827,7 @@ // @ts-ignore let children = ""; let str = ""; - let previousStr = ""; + // let previousStr: string = ""; const indent = indents[level]; const indentSub = indents[level + 1]; switch (data.typ) { @@ -24836,6 +24854,9 @@ str = options.newLine + str; } children += str; + if (sourcemaps != null && str !== "" && options.newLine) { + move(sourceLocation, linesMap, options.newLine); + } } return children; case exports.EnumToken.AtRuleNodeType: @@ -24845,13 +24866,16 @@ if ([exports.EnumToken.AtRuleNodeType, exports.EnumToken.KeyframesAtRuleNodeType].includes(data.typ) && !("chi" in data)) { return `${indent}@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val};`; } - const prelude = [exports.EnumToken.AtRuleNodeType, exports.EnumToken.KeyframesAtRuleNodeType].includes(data.typ) - ? `@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val}${options.indent}{` - : data.sel + `${options.indent}{`; + const prelude = (indent.length > 0 ? options.newLine : "") + + indent + + ([exports.EnumToken.AtRuleNodeType, exports.EnumToken.KeyframesAtRuleNodeType].includes(data.typ) + ? `@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val}${options.indent}{` + : data.sel + `${options.indent}{`); if (sourcemaps != null) { updateSourceMap(data, options, cache, sourcemaps, sourceLocation, linesMap, prelude); } let node; + let recordDeclarationSourceMap = data.typ == exports.EnumToken.AtRuleNodeType; for (let i = 0; i < data.chi.length; i++) { node = data.chi[i]; if (node.typ == exports.EnumToken.CommentNodeType) { @@ -24876,38 +24900,47 @@ : node.val) .reduce(reducer, "") .trimEnd()};`; - if (sourcemaps != null) { - if (previousStr.length > 0) { - move(sourceLocation, linesMap, previousStr); - } - } - previousStr = str === "" ? "" : options.newLine + indentSub + str; } - // else if (node.typ == EnumToken.AtRuleNodeType && !("chi" in node)) { - // str = `${(node).val === "" ? "" : options.indent || " "}${(node).val};`; - // } else { - if (sourcemaps != null) { - if (previousStr.length > 0) { - move(sourceLocation, linesMap, previousStr); - } - } str = renderAstNode(node, options, sourcemaps, sourceLocation, linesMap, errors, reducer, cache, level + 1, indents); - previousStr = ""; + if (str === "") { + continue; + } + children += str; + str = ""; + continue; } if (str === "") { continue; } str = options.newLine + indentSub + str; children += str; - } - if (sourcemaps != null && str !== "") { - move(sourceLocation, linesMap, str.endsWith(";") ? str.slice(0, -1) : str); + if (sourcemaps != null && str !== "") { + move(sourceLocation, linesMap, str); + if (node.typ == exports.EnumToken.DeclarationNodeType && recordDeclarationSourceMap) { + // if declaration is child of at-rule, then record it + // .rule { + // @media screen { + // color: red; + // } + // } + const source = options.sourcesMap.get(node[LOC].srcId); + sourcemaps.push([ + ...linesMap.getOffsets(sourceLocation.end - str.length + options.newLine.length + indentSub.length), + node[LOC].srcId, + ...source.getOffsets(node[LOC].sta), + source.getFileName(), + source.getContent(), + ]); + } + } } if (children.endsWith(";")) { children = children.slice(0, -1); + sourceLocation.end--; } if (options.removeEmpty && children === "") { + sourceLocation.end -= prelude.length; return ""; } const end = options.newLine + indent + `}`; @@ -29717,7 +29750,7 @@ }; let tokens = []; let context = ast; - ast[ROOT] = ast; + // ast[ROOT] = ast; ast[LOC] = { sta: 0, end: 0, @@ -29999,6 +30032,20 @@ } let replacement; let callable; + while (stack.length > 0 && context != ast) { + const previousNode = stack.pop(); + context = (stack[stack.length - 1] ?? ast); + previousNode[PARENT] = context; + // remove empty nodes + if (options.removeEmpty && + previousNode != null && + previousNode.chi.length == 0 && + context.chi[context.chi.length - 1] == previousNode) { + context.chi.pop(); + continue; + } + break; + } if (options.visitor != null) { let parens; for (const result of walk(ast)) { @@ -30224,19 +30271,6 @@ } } } - while (stack.length > 0 && context != ast) { - const previousNode = stack.pop(); - context = (stack[stack.length - 1] ?? ast); - // remove empty nodes - if (options.removeEmpty && - previousNode != null && - previousNode.chi.length == 0 && - context.chi[context.chi.length - 1] == previousNode) { - context.chi.pop(); - continue; - } - break; - } if (options.minify) { if (ast.chi.length > 0) { let passes = options.pass ?? 1; diff --git a/dist/index.cjs b/dist/index.cjs index fb531363..91dd92da 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -21729,9 +21729,9 @@ class LineMap { if (offset < 0 || line < 0) { return [1, 1]; } - const column = offset - this.lineStarts[line]; + const column = offset - this.lineStarts[line] + 1; // [line, column] - return [line + 1, line === 0 ? column + 1 : column]; + return [line + 1, column == 0 ? 1 : column]; } /** * search the greatest index of the value less than or equal to offset @@ -24140,11 +24140,16 @@ function reduceRuleSelector(node) { */ function expand(ast) { const result = { ...ast, chi: [] }; + let children; for (let i = 0; i < ast.chi.length; i++) { - const node = ast.chi[i]; + let node = ast.chi[i]; if (node.typ === exports.EnumToken.RuleNodeType) { + children = expandRule(node); + for (const child of children) { + child[PARENT] = result; + } // @ts-ignore - result.chi.push(...expandRule(node)); + result.chi.push(...children); } else if (node.typ == exports.EnumToken.AtRuleNodeType && "chi" in node) { let hasRule = false; @@ -24156,10 +24161,23 @@ function expand(ast) { break; } } - // @ts-ignore - result.chi.push({ ...(hasRule ? expand(node) : node) }); + if (hasRule) { + node = expand(node); + for (const child of node.chi) { + child[PARENT] = result; + } + node[PARENT] = result; + // @ts-ignore + result.chi.push(node); + } + else { + node[PARENT] = result; + // @ts-ignore + result.chi.push(node); + } } else { + node[PARENT] = result; // @ts-ignore result.chi.push(node); } @@ -24812,7 +24830,7 @@ function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, erro // @ts-ignore let children = ""; let str = ""; - let previousStr = ""; + // let previousStr: string = ""; const indent = indents[level]; const indentSub = indents[level + 1]; switch (data.typ) { @@ -24839,6 +24857,9 @@ function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, erro str = options.newLine + str; } children += str; + if (sourcemaps != null && str !== "" && options.newLine) { + move(sourceLocation, linesMap, options.newLine); + } } return children; case exports.EnumToken.AtRuleNodeType: @@ -24848,13 +24869,16 @@ function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, erro if ([exports.EnumToken.AtRuleNodeType, exports.EnumToken.KeyframesAtRuleNodeType].includes(data.typ) && !("chi" in data)) { return `${indent}@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val};`; } - const prelude = [exports.EnumToken.AtRuleNodeType, exports.EnumToken.KeyframesAtRuleNodeType].includes(data.typ) - ? `@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val}${options.indent}{` - : data.sel + `${options.indent}{`; + const prelude = (indent.length > 0 ? options.newLine : "") + + indent + + ([exports.EnumToken.AtRuleNodeType, exports.EnumToken.KeyframesAtRuleNodeType].includes(data.typ) + ? `@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val}${options.indent}{` + : data.sel + `${options.indent}{`); if (sourcemaps != null) { updateSourceMap(data, options, cache, sourcemaps, sourceLocation, linesMap, prelude); } let node; + let recordDeclarationSourceMap = data.typ == exports.EnumToken.AtRuleNodeType; for (let i = 0; i < data.chi.length; i++) { node = data.chi[i]; if (node.typ == exports.EnumToken.CommentNodeType) { @@ -24879,38 +24903,47 @@ function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, erro : node.val) .reduce(reducer, "") .trimEnd()};`; - if (sourcemaps != null) { - if (previousStr.length > 0) { - move(sourceLocation, linesMap, previousStr); - } - } - previousStr = str === "" ? "" : options.newLine + indentSub + str; } - // else if (node.typ == EnumToken.AtRuleNodeType && !("chi" in node)) { - // str = `${(node).val === "" ? "" : options.indent || " "}${(node).val};`; - // } else { - if (sourcemaps != null) { - if (previousStr.length > 0) { - move(sourceLocation, linesMap, previousStr); - } - } str = renderAstNode(node, options, sourcemaps, sourceLocation, linesMap, errors, reducer, cache, level + 1, indents); - previousStr = ""; + if (str === "") { + continue; + } + children += str; + str = ""; + continue; } if (str === "") { continue; } str = options.newLine + indentSub + str; children += str; - } - if (sourcemaps != null && str !== "") { - move(sourceLocation, linesMap, str.endsWith(";") ? str.slice(0, -1) : str); + if (sourcemaps != null && str !== "") { + move(sourceLocation, linesMap, str); + if (node.typ == exports.EnumToken.DeclarationNodeType && recordDeclarationSourceMap) { + // if declaration is child of at-rule, then record it + // .rule { + // @media screen { + // color: red; + // } + // } + const source = options.sourcesMap.get(node[LOC].srcId); + sourcemaps.push([ + ...linesMap.getOffsets(sourceLocation.end - str.length + options.newLine.length + indentSub.length), + node[LOC].srcId, + ...source.getOffsets(node[LOC].sta), + source.getFileName(), + source.getContent(), + ]); + } + } } if (children.endsWith(";")) { children = children.slice(0, -1); + sourceLocation.end--; } if (options.removeEmpty && children === "") { + sourceLocation.end -= prelude.length; return ""; } const end = options.newLine + indent + `}`; @@ -29720,7 +29753,7 @@ async function doParse(iter, options = {}) { }; let tokens = []; let context = ast; - ast[ROOT] = ast; + // ast[ROOT] = ast; ast[LOC] = { sta: 0, end: 0, @@ -30002,6 +30035,20 @@ async function doParse(iter, options = {}) { } let replacement; let callable; + while (stack.length > 0 && context != ast) { + const previousNode = stack.pop(); + context = (stack[stack.length - 1] ?? ast); + previousNode[PARENT] = context; + // remove empty nodes + if (options.removeEmpty && + previousNode != null && + previousNode.chi.length == 0 && + context.chi[context.chi.length - 1] == previousNode) { + context.chi.pop(); + continue; + } + break; + } if (options.visitor != null) { let parens; for (const result of walk(ast)) { @@ -30227,19 +30274,6 @@ async function doParse(iter, options = {}) { } } } - while (stack.length > 0 && context != ast) { - const previousNode = stack.pop(); - context = (stack[stack.length - 1] ?? ast); - // remove empty nodes - if (options.removeEmpty && - previousNode != null && - previousNode.chi.length == 0 && - context.chi[context.chi.length - 1] == previousNode) { - context.chi.pop(); - continue; - } - break; - } if (options.minify) { if (ast.chi.length > 0) { let passes = options.pass ?? 1; diff --git a/dist/lib/ast/expand.js b/dist/lib/ast/expand.js index 577eefeb..4da8070f 100644 --- a/dist/lib/ast/expand.js +++ b/dist/lib/ast/expand.js @@ -1,5 +1,5 @@ import { splitRule } from './minify.js'; -import { combinators, RAW } from '../syntax/constants.js'; +import { PARENT, combinators, RAW } from '../syntax/constants.js'; import { parseString } from '../parser/parse.js'; import { walkValues } from './walk.js'; import { renderValue } from '../renderer/render.js'; @@ -13,11 +13,16 @@ import { EnumToken } from './types.js'; */ function expand(ast) { const result = { ...ast, chi: [] }; + let children; for (let i = 0; i < ast.chi.length; i++) { - const node = ast.chi[i]; + let node = ast.chi[i]; if (node.typ === EnumToken.RuleNodeType) { + children = expandRule(node); + for (const child of children) { + child[PARENT] = result; + } // @ts-ignore - result.chi.push(...expandRule(node)); + result.chi.push(...children); } else if (node.typ == EnumToken.AtRuleNodeType && "chi" in node) { let hasRule = false; @@ -29,10 +34,23 @@ function expand(ast) { break; } } - // @ts-ignore - result.chi.push({ ...(hasRule ? expand(node) : node) }); + if (hasRule) { + node = expand(node); + for (const child of node.chi) { + child[PARENT] = result; + } + node[PARENT] = result; + // @ts-ignore + result.chi.push(node); + } + else { + node[PARENT] = result; + // @ts-ignore + result.chi.push(node); + } } else { + node[PARENT] = result; // @ts-ignore result.chi.push(node); } diff --git a/dist/lib/parser/linesmap.js b/dist/lib/parser/linesmap.js index 38daa8f7..38c9fad1 100644 --- a/dist/lib/parser/linesmap.js +++ b/dist/lib/parser/linesmap.js @@ -26,9 +26,9 @@ class LineMap { if (offset < 0 || line < 0) { return [1, 1]; } - const column = offset - this.lineStarts[line]; + const column = offset - this.lineStarts[line] + 1; // [line, column] - return [line + 1, line === 0 ? column + 1 : column]; + return [line + 1, column == 0 ? 1 : column]; } /** * search the greatest index of the value less than or equal to offset diff --git a/dist/lib/parser/parse.js b/dist/lib/parser/parse.js index 99b36382..64cc0afa 100644 --- a/dist/lib/parser/parse.js +++ b/dist/lib/parser/parse.js @@ -6,7 +6,7 @@ import { minify } from '../ast/minify.js'; import { expand } from '../ast/expand.js'; import { WalkerEvent, walk, walkValues } from '../ast/walk.js'; import { tokenizeStream, tokenize } from './tokenize.js'; -import { ROOT, LOC, tokensfuncDefMap, STATE, PARENT, TOKENS, ERRORS, pageMarginBoxType } from '../syntax/constants.js'; +import { LOC, tokensfuncDefMap, STATE, PARENT, TOKENS, ROOT, ERRORS, pageMarginBoxType } from '../syntax/constants.js'; import { hashAlgorithms, hash, syncHash } from './utils/hash.js'; import { parseSelector } from './utils/selector.js'; import { parseDeclaration } from './utils/declaration.js'; @@ -1380,7 +1380,7 @@ async function doParse(iter, options = {}) { }; let tokens = []; let context = ast; - ast[ROOT] = ast; + // ast[ROOT] = ast; ast[LOC] = { sta: 0, end: 0, @@ -1662,6 +1662,20 @@ async function doParse(iter, options = {}) { } let replacement; let callable; + while (stack.length > 0 && context != ast) { + const previousNode = stack.pop(); + context = (stack[stack.length - 1] ?? ast); + previousNode[PARENT] = context; + // remove empty nodes + if (options.removeEmpty && + previousNode != null && + previousNode.chi.length == 0 && + context.chi[context.chi.length - 1] == previousNode) { + context.chi.pop(); + continue; + } + break; + } if (options.visitor != null) { let parens; for (const result of walk(ast)) { @@ -1887,19 +1901,6 @@ async function doParse(iter, options = {}) { } } } - while (stack.length > 0 && context != ast) { - const previousNode = stack.pop(); - context = (stack[stack.length - 1] ?? ast); - // remove empty nodes - if (options.removeEmpty && - previousNode != null && - previousNode.chi.length == 0 && - context.chi[context.chi.length - 1] == previousNode) { - context.chi.pop(); - continue; - } - break; - } if (options.minify) { if (ast.chi.length > 0) { let passes = options.pass ?? 1; diff --git a/dist/lib/renderer/render.js b/dist/lib/renderer/render.js index d96ed529..f7710816 100644 --- a/dist/lib/renderer/render.js +++ b/dist/lib/renderer/render.js @@ -254,7 +254,7 @@ function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, erro // @ts-ignore let children = ""; let str = ""; - let previousStr = ""; + // let previousStr: string = ""; const indent = indents[level]; const indentSub = indents[level + 1]; switch (data.typ) { @@ -281,6 +281,9 @@ function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, erro str = options.newLine + str; } children += str; + if (sourcemaps != null && str !== "" && options.newLine) { + move(sourceLocation, linesMap, options.newLine); + } } return children; case EnumToken.AtRuleNodeType: @@ -290,13 +293,16 @@ function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, erro if ([EnumToken.AtRuleNodeType, EnumToken.KeyframesAtRuleNodeType].includes(data.typ) && !("chi" in data)) { return `${indent}@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val};`; } - const prelude = [EnumToken.AtRuleNodeType, EnumToken.KeyframesAtRuleNodeType].includes(data.typ) - ? `@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val}${options.indent}{` - : data.sel + `${options.indent}{`; + const prelude = (indent.length > 0 ? options.newLine : "") + + indent + + ([EnumToken.AtRuleNodeType, EnumToken.KeyframesAtRuleNodeType].includes(data.typ) + ? `@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val}${options.indent}{` + : data.sel + `${options.indent}{`); if (sourcemaps != null) { updateSourceMap(data, options, cache, sourcemaps, sourceLocation, linesMap, prelude); } let node; + let recordDeclarationSourceMap = data.typ == EnumToken.AtRuleNodeType; for (let i = 0; i < data.chi.length; i++) { node = data.chi[i]; if (node.typ == EnumToken.CommentNodeType) { @@ -321,38 +327,47 @@ function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, erro : node.val) .reduce(reducer, "") .trimEnd()};`; - if (sourcemaps != null) { - if (previousStr.length > 0) { - move(sourceLocation, linesMap, previousStr); - } - } - previousStr = str === "" ? "" : options.newLine + indentSub + str; } - // else if (node.typ == EnumToken.AtRuleNodeType && !("chi" in node)) { - // str = `${(node).val === "" ? "" : options.indent || " "}${(node).val};`; - // } else { - if (sourcemaps != null) { - if (previousStr.length > 0) { - move(sourceLocation, linesMap, previousStr); - } - } str = renderAstNode(node, options, sourcemaps, sourceLocation, linesMap, errors, reducer, cache, level + 1, indents); - previousStr = ""; + if (str === "") { + continue; + } + children += str; + str = ""; + continue; } if (str === "") { continue; } str = options.newLine + indentSub + str; children += str; - } - if (sourcemaps != null && str !== "") { - move(sourceLocation, linesMap, str.endsWith(";") ? str.slice(0, -1) : str); + if (sourcemaps != null && str !== "") { + move(sourceLocation, linesMap, str); + if (node.typ == EnumToken.DeclarationNodeType && recordDeclarationSourceMap) { + // if declaration is child of at-rule, then record it + // .rule { + // @media screen { + // color: red; + // } + // } + const source = options.sourcesMap.get(node[LOC].srcId); + sourcemaps.push([ + ...linesMap.getOffsets(sourceLocation.end - str.length + options.newLine.length + indentSub.length), + node[LOC].srcId, + ...source.getOffsets(node[LOC].sta), + source.getFileName(), + source.getContent(), + ]); + } + } } if (children.endsWith(";")) { children = children.slice(0, -1); + sourceLocation.end--; } if (options.removeEmpty && children === "") { + sourceLocation.end -= prelude.length; return ""; } const end = options.newLine + indent + `}`; diff --git a/files/index.md b/files/index.md index 9b88907c..3bd1279c 100644 --- a/files/index.md +++ b/files/index.md @@ -9,6 +9,7 @@ children: - ./css-module.md - ./minification.md - ./transform.md + - ./sourcemap.md - ./syntax-lowering.md - ./ast.md - ./utilities.md @@ -22,6 +23,7 @@ children: - [CSS Modules](./css-module.md) - [Minification](./minification.md) - [Custom Transform](./transform.md) +- [Sourcemap](./sourcemap.md) - [Syntax Lowering](./syntax-lowering.md) - [Ast Manipulation](./ast.md) - [Utility Functions](./utilities.md) diff --git a/files/minification.md b/files/minification.md index f58f8b7d..4d329fac 100644 --- a/files/minification.md +++ b/files/minification.md @@ -891,7 +891,7 @@ Output: ### Computed shorthands properties -Below is the list of computed shorthands properties: +Below is the list of computed shorthands properties. Minification is fully supported for the propertie with a checkmark. - [ ] ~all~ - [x] animation diff --git a/files/sourcemap.md b/files/sourcemap.md new file mode 100644 index 00000000..157f55a1 --- /dev/null +++ b/files/sourcemap.md @@ -0,0 +1,111 @@ +--- +title: Sourcemap +group: Documents +category: Guides +--- + + +# Sourcemaps + +**CSS-Parser** supports generating sourcemaps. To enable it, you must pass `sourcemap: true` or `sourcemap: 'inline`. +When the `output` parameter is provided, sourcemap file paths are resolved relative to the specified output file. + +```ts + +import {transform} from '@tbela99/css-parser'; + +const css = ` +@import 'styles.css'; +button { + background: linear-gradient( + if(media(min-width: 768px): to right; else: to bottom), + if(style(--dark-mode): #333; else: #fff), + if(style(--dark-mode): #000; else: #ccc) + ); +}`; + +result = await transform(css, { + + beautify: true, + sourcemap: true, + resolveImport: true, + output: 'dist/doc.html' +}); + +console.log(result.map.toJSON()); +``` + +### Input sourcemap + +If the input CSS comes from another tool, you can pass the sourcemap content to link the generated CSS positions to the original files. Additionally, if an inline sourcemap is provided with the CSS input, it will be automatically used as the input sourcemap. + + +```ts + +import {transform} from '@tbela99/css-parser'; + +const css = ` +table.colortable { + width: 100%; + text-shadow: none; + border-collapse: collapse; + & td { + text-align: center; + &.c { + text-transform: uppercase; + background: color(display-p3-linear 1 1 .08948) + } + } + & th { + text-align: center; + color: color(display-p3-linear .038323 .208695 .015628); + font-weight: 400; + padding: 2px 3px + } + & td,& th { + border: 1px solid color(display-p3-linear .695155 .700862 .720967); + padding: 5px + } +} +.foo { + color: color(display-p3-linear 0 0 .91052); + & { + padding: 2ch; + color: color(display-p3-linear 0 0 .91052); + && { + padding: 2ch + } + } +} +h1 { + text-transform: uppercase +} +button { + background: linear-gradient(color(display-p3-linear 1 1 1),color(display-p3-linear .603827 .603827 .603827)); + @media (min-width:768px) { + background: linear-gradient(90deg,color(display-p3-linear 1 1 1),color(display-p3-linear .603827 .603827 .603827)); + @container style(--dark-mode) { + background: linear-gradient(90deg,color(display-p3-linear .033105 .033105 .033105),color(display-p3-linear .603827 .603827 .603827)); + background: linear-gradient(90deg,color(display-p3-linear .033105 .033105 .033105),color(display-p3-linear 0 0 0)) + } + } + @container style(--dark-mode) { + background: linear-gradient(color(display-p3-linear .033105 .033105 .033105),color(display-p3-linear .603827 .603827 .603827)); + background: linear-gradient(color(display-p3-linear .033105 .033105 .033105),color(display-p3-linear 0 0 0)) + } +} +/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5lc3RlZC5jc3MiLG51bGxdLCJzb3VyY2VzQ29udGVudCI6W251bGwsIlxuQGltcG9ydCAnLi90ZXN0L25lc3RlZC5jc3MnO1xuaDEge1xuICB0ZXh0LXRyYW5zZm9ybTogdXBwZXJjYXNlO1xufVxuYnV0dG9uIHtcblx0YmFja2dyb3VuZDogbGluZWFyLWdyYWRpZW50KFxuXHRcdGlmKG1lZGlhKG1pbi13aWR0aDogNzY4cHgpOiB0byByaWdodDsgZWxzZTogdG8gYm90dG9tKSxcblx0XHRpZihzdHlsZSgtLWRhcmstbW9kZSk6ICMzMzM7IGVsc2U6ICNmZmYpLFxuXHRcdGlmKHN0eWxlKC0tZGFyay1tb2RlKTogIzAwMDsgZWxzZTogI2NjYylcblx0KTtcbn1cbiAgICAiXSwibWFwcGluZ3MiOiJBQUFBOzs7MEJBSUk7b0JBRUk7Ozs7Ozs7O0NBS0o7Ozs7O0NBTUE7Ozs7Ozs7O0FBTUo7MkNBRUk7OzRDQUdJOzs7Ozs7Ozs7Ozs7OztBQzFCUjs7QUNHQTs2R0NDQztvSENBQTs7Ozs7Ozs7Q0NBQSJ9 */ +`; + +result = await transform(css, { + + beautify: true, + sourcemap: true, + output: 'dist/doc.html' +}); + +console.log(result.map.toJSON()); +``` + +------ +[← Custom Transform](./transform.md) | [Syntax Lowering →](./syntax-lowering.md) \ No newline at end of file diff --git a/files/syntax-lowering.md b/files/syntax-lowering.md index 8f4c6bed..f070397f 100644 --- a/files/syntax-lowering.md +++ b/files/syntax-lowering.md @@ -135,4 +135,4 @@ table.colortable th { ```` ------ -[← Custom Transform](./transform.md) | [Ast Manipulation →](./ast.md) \ No newline at end of file +[← Custom Transform](./sourcemap.md) | [Ast Manipulation →](./ast.md) \ No newline at end of file diff --git a/files/transform.md b/files/transform.md index 3545bc28..92b10cc5 100644 --- a/files/transform.md +++ b/files/transform.md @@ -446,9 +446,9 @@ console.debug(await transform(css, options)); // body {color:#f3fff0} ``` -### Example of visitor that inlines images +### Example of plugin -A visitor that inlines all images under a specific size +A plugin implemented as visitor that inlines all images under a specific size. ```ts import { @@ -463,76 +463,79 @@ import { AstDeclaration, AstNode } from "@tbela99/css-parser"; -const css = ` -.goal .bg-indigo { - background: url(/img/animatecss-opengraph.jpg); -} -`; - -// 35 kb or something -const maxSize = 35 * 1024; -// accepted images -const extensions = ['jpg', 'gif', 'png', 'webp'] -const result = await transform(css, { - visitor: { - UrlFunctionTokenType: async (node: FunctionURLToken, parent : AstNode) => { - if (parent.typ == EnumToken.DeclarationNodeType) { - - const t = node.chi.find(t => t.typ != EnumToken.WhitespaceTokenType && t.typ != EnumToken.CommaTokenType) as Token; - - if (t == null) { - - return; - } - - const url = t.typ == EnumToken.StringTokenType ? (t as StringToken).val.slice(1, -1) : (t as UrlToken).val; - - if (url.startsWith('data:')) { +function toBase64(arraybuffer: Uint8Array) { + // @ts-ignore + if (typeof Uint8Array.prototype.toBase64! == "function") { + // @ts-ignore + return arraybuffer.toBase64(); + } - return; - } + let binary = ""; + for (const byte of arraybuffer) { + binary += String.fromCharCode(byte); + } - const matches = /(.*?\/)?([^/.]+)\.([^?#]+)([?#].*)?$/.exec(url); + return btoa(binary); +} - if (matches == null || !extensions.includes(matches[3].toLowerCase())) { +function inlineImagesPlugin(maxSize: number, extensions: string[]) { + return async function (node: FunctionURLToken, parent: AstNode) { + if (parent.typ == EnumToken.DeclarationNodeType) { + const t = node.chi.find( + (t) => t.typ != EnumToken.WhitespaceTokenType && t.typ != EnumToken.CommaTokenType, + ) as Token; - return; - } + if (t == null) { + return; + } - const buffer = await load(url, '.', ResponseType.ArrayBuffer) as ArrayBuffer ; + const url = t.typ == EnumToken.StringTokenType ? (t as StringToken).val.slice(1, -1) : (t as UrlToken).val; - if (buffer.byteLength > maxSize) { + if (url.startsWith("data:")) { + return; + } - return - } + const matches = /(.*?\/)?([^/.]+)\.([^?#]+)([?#].*)?$/.exec(url); - Object.assign(t, {typ: EnumToken.StringTokenType, val: `"data:image/${matches[3].toLowerCase()};base64,${toBase64(new Uint8Array(buffer))}"`}) + if (matches == null || !extensions.includes(matches[3].toLowerCase())) { + return; } - } - } -}); - -function toBase64(arraybuffer: Uint8Array) { - // @ts-ignore - if (typeof Uint8Array.prototype.toBase64! == 'function') { + const buffer = (await load(url, ".", ResponseType.ArrayBuffer)) as ArrayBuffer; - // @ts-ignore - return arraybuffer.toBase64(); - } + if (buffer.byteLength > maxSize) { + return; + } - let binary = ''; - for (const byte of arraybuffer) { - binary += String.fromCharCode( byte); - } + // change node type to EnumToken.String + Object.assign(t, { + typ: EnumToken.StringTokenType, + val: `"data:image/${matches[3].toLowerCase()};base64,${toBase64(new Uint8Array(buffer))}"`, + }); + } + }; +} - return btoa( binary ); +// 35 kb or something +const maxSize = 35 * 1024; +// accepted images +const extensions = ["jpg", "gif", "png", "webp"]; +const css = ` +.goal .bg-indigo { + background: url(/img/animatecss-opengraph.jpg); } +`; + +const result = await transform(css, { + visitor: { + UrlFunctionTokenType: inlineImagesPlugin(maxSize, extensions), + }, +}); console.error(result.code); // .goal .bg-indigo{background:url("data:image/jpg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/4QugRXhpZgAA ...")} ``` ------ -[← Minification](./minification.md) | [Syntax Lowering →](./syntax-lowering.md) \ No newline at end of file +[← Minification](./minification.md) | [Sourcemap →](./sourcemap.md) \ No newline at end of file diff --git a/files/usage.md b/files/usage.md index ffbc6107..9640acf6 100644 --- a/files/usage.md +++ b/files/usage.md @@ -380,35 +380,6 @@ button { } } ``` -## Sourcemaps - -**CSS-Parser** supports generating sourcemaps. When the `output` parameter is provided, sourcemap file paths are resolved relative to the specified output file. - - -```ts - -import {transform} from '@tbela99/css-parser'; - -const css = ` -@import 'styles.css'; -button { - background: linear-gradient( - if(media(min-width: 768px): to right; else: to bottom), - if(style(--dark-mode): #333; else: #fff), - if(style(--dark-mode): #000; else: #ccc) - ); -}`; - -result = await transform(css, { - - beautify: true, - sourcemap: true, - resolveImport: true, - output: 'dist/doc.html' -}); - -console.log(result.map.toJSON()); -``` ## Difference Between Sync and Async APIs diff --git a/jsr.json b/jsr.json index 34aff9f9..3cf6e541 100644 --- a/jsr.json +++ b/jsr.json @@ -1,6 +1,6 @@ { "name": "@tbela99/css-parser", - "version": "1.4.11", + "version": "1.5.0-alpha.1", "publish": { "include": [ "src", diff --git a/package.json b/package.json index 3c22ae62..165717ee 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@tbela99/css-parser", "description": "CSS parser, minifier and validator for node and the browser", - "version": "1.4.11", + "version": "1.5.0-alpha.1", "exports": { ".": "./dist/node.js", "./node": "./dist/node.js", diff --git a/src/lib/ast/expand.ts b/src/lib/ast/expand.ts index d975052d..b2687841 100644 --- a/src/lib/ast/expand.ts +++ b/src/lib/ast/expand.ts @@ -1,10 +1,10 @@ -import {splitRule} from "./minify.ts"; -import {combinators, RAW} from "../syntax/constants.ts"; -import {parseString} from "../parser/parse.ts"; -import {walkValues} from "./walk.ts"; -import {renderValue} from "../renderer/render.ts"; -import type {AstAtRule, AstNode, AstRule, AstStyleSheet, LiteralToken, Token} from "../../@types/index.d.ts"; -import {EnumToken} from "./types.ts"; +import { splitRule } from "./minify.ts"; +import { combinators, PARENT, RAW } from "../syntax/constants.ts"; +import { parseString } from "../parser/parse.ts"; +import { walkValues } from "./walk.ts"; +import { renderValue } from "../renderer/render.ts"; +import type { AstAtRule, AstNode, AstRule, AstStyleSheet, LiteralToken, Token } from "../../@types/index.d.ts"; +import { EnumToken } from "./types.ts"; /** * expand css nesting ast nodes @@ -14,13 +14,20 @@ import {EnumToken} from "./types.ts"; */ export function expand(ast: AstStyleSheet | AstAtRule | AstRule): AstNode { const result = { ...ast, chi: [] }; + let children: AstNode[]; for (let i = 0; i < ast.chi!.length; i++) { - const node = ast.chi![i]; + let node = ast.chi![i]; if (node.typ === EnumToken.RuleNodeType) { + children = expandRule(node as AstRule); + + for (const child of children) { + child[PARENT] = result; + } + // @ts-ignore - result.chi.push(...expandRule(node)); + result.chi.push(...children); } else if (node.typ == EnumToken.AtRuleNodeType && "chi" in node) { let hasRule: boolean = false; let j: number = node!.chi!.length; @@ -33,10 +40,25 @@ export function expand(ast: AstStyleSheet | AstAtRule | AstRule): AstNode { } } - // @ts-ignore - result.chi.push({ ...(hasRule ? expand(node) : node) }); + if (hasRule) { + node = expand(node as AstRule); + + for (const child of node.chi) { + child[PARENT] = result; + } + + node[PARENT] = result; + // @ts-ignore + result.chi.push(node); + } else { + node[PARENT] = result; + // @ts-ignore + result.chi.push(node); + } } else { + node[PARENT] = result; // @ts-ignore + result.chi!.push(node); } } @@ -85,8 +107,7 @@ function expandRule(node: AstRule): Array { [], ) .join(","); - - } else { + } else { let childSelectorCompound: string[] = []; let withCompound: string[] = []; let withoutCompound: string[] = []; @@ -103,7 +124,7 @@ function expandRule(node: AstRule): Array { continue; } - for (const sel of rule[RAW]?? splitRule(rule.sel)) { + for (const sel of rule[RAW] ?? splitRule(rule.sel)) { const s: string = sel.join(""); if (s.includes("&") || parentSelector) { diff --git a/src/lib/ast/minify.ts b/src/lib/ast/minify.ts index 5498a5b0..f281a946 100644 --- a/src/lib/ast/minify.ts +++ b/src/lib/ast/minify.ts @@ -1,7 +1,7 @@ -import {eq} from "../parser/utils/eq.ts"; -import {doRender, renderValue} from "../renderer/render.ts"; +import { eq } from "../parser/utils/eq.ts"; +import { doRender, renderValue } from "../renderer/render.ts"; import * as allFeatures from "./features/index.ts"; -import {walkValues} from "./walk.ts"; +import { walkValues } from "./walk.ts"; import type { AstAtRule, AstDeclaration, @@ -24,15 +24,15 @@ import type { RawSelectorTokens, Token, } from "../../@types/index.d.ts"; -import {EnumToken} from "./types.ts"; -import {isFunction, isIdent, isIdentStart, isWhiteSpace} from "../syntax/syntax.ts"; -import {FeatureWalkMode} from "./features/type.ts"; -import {trimArray} from "../validation/match.ts"; -import {combinators, LOC, OPTIMIZED, PARENT, RAW, TOKENS} from "../syntax/constants.ts"; -import {replaceNodeOrValue} from "../parser/utils/token.ts"; -import {parseString} from "../parser/parse.ts"; -import {tokenize} from "../parser/tokenize.ts"; -import {replaceCompound} from "./expand.ts"; +import { EnumToken } from "./types.ts"; +import { isFunction, isIdent, isIdentStart, isWhiteSpace } from "../syntax/syntax.ts"; +import { FeatureWalkMode } from "./features/type.ts"; +import { trimArray } from "../validation/match.ts"; +import { combinators, LOC, OPTIMIZED, PARENT, RAW, TOKENS } from "../syntax/constants.ts"; +import { replaceNodeOrValue } from "../parser/utils/token.ts"; +import { parseString } from "../parser/parse.ts"; +import { tokenize } from "../parser/tokenize.ts"; +import { replaceCompound } from "./expand.ts"; const notEndingWith: string[] = ["(", "["].concat(combinators); const rules: EnumToken[] = [ @@ -91,7 +91,7 @@ export function minify( let replacement: AstNode | null; // @ts-ignore - let {sourcemap, module, ...options} = opt; + let { sourcemap, module, ...options } = opt; if (!("features" in options)) { // @ts-ignore @@ -209,7 +209,7 @@ export function minify( const result = feature.run( replacement as AstRule | AstAtRule, options, - parent[PARENT] ?? ast, + parent[PARENT] ?? (ast as AstRule | AstAtRule | AstStyleSheet), context, FeatureWalkMode.Post, ); @@ -1748,7 +1748,7 @@ function diff(n1: AstRule, n2: AstRule, options: ParserOptions = {}) { chi: intersect.reverse(), }; - let op = {level: 0, ...options}; + let op = { level: 0, ...options }; if ( result == null || @@ -1773,11 +1773,10 @@ function diff(n1: AstRule, n2: AstRule, options: ParserOptions = {}) { return curr.chi.length == 0 ? acc : acc + css.length; }, 0) <= [node1, node2, result].reduce((acc: number, curr: AstRule): number => { - let css: string = options.cache!.get(curr) as string; if (css != null) { - return curr.chi.length == 0 ? acc : acc + css.length + return curr.chi.length == 0 ? acc : acc + css.length; } let level: number = 0; @@ -1788,8 +1787,8 @@ function diff(n1: AstRule, n2: AstRule, options: ParserOptions = {}) { parent = parent[PARENT] as AstRule; } - op.level = level; - css = doRender(curr, op).code; + op.level = level; + css = doRender(curr, op).code; return curr.chi.length == 0 ? acc : acc + css.length; }, 0) diff --git a/src/lib/parser/linesmap.ts b/src/lib/parser/linesmap.ts index 8f5fa34b..9356d0ea 100644 --- a/src/lib/parser/linesmap.ts +++ b/src/lib/parser/linesmap.ts @@ -31,9 +31,9 @@ export class LineMap { return [1, 1]; } - const column: number = offset - this.lineStarts[line]; + const column: number = offset - this.lineStarts[line] + 1; // [line, column] - return [line + 1, line === 0 ? column + 1 : column]; + return [line + 1, column == 0 ? 1 : column]; } /** diff --git a/src/lib/parser/parse.ts b/src/lib/parser/parse.ts index 6e85e4d9..3bfa7bad 100644 --- a/src/lib/parser/parse.ts +++ b/src/lib/parser/parse.ts @@ -1838,7 +1838,7 @@ export async function doParse( let tokens: Token[] = []; let context: AstRuleList = ast; - ast[ROOT] = ast; + // ast[ROOT] = ast; ast[LOC] = { sta: 0, @@ -2020,7 +2020,6 @@ export async function doParse( : // @ts-expect-error ((iter as Iterator).next().value as TokenizeResult)) ) { - stats.bytesIn = item.bytesIn; stats.tokensCount++; @@ -2156,7 +2155,10 @@ export async function doParse( const url: string = token.typ == EnumToken.StringTokenType ? token.val.slice(1, -1) : token.val; try { - const src = options.resolve!(url, options.src ? dirname(options.src as string) : (options.cwd as string)) as ResolvedPath; + const src = options.resolve!( + url, + options.src ? dirname(options.src as string) : (options.cwd as string), + ) as ResolvedPath; const result = options.load!(src) as LoadResult; const stream = result instanceof Promise || Object.getPrototypeOf(result).constructor.name == "AsyncFunction" @@ -2208,6 +2210,26 @@ export async function doParse( let replacement: GenericVisitorResult; let callable: GenericVisitorHandler; + while (stack.length > 0 && context != ast) { + const previousNode: AstAtRule | AstRule = stack.pop() as AstAtRule | AstRule; + context = (stack[stack.length - 1] ?? ast) as AstRuleList; + + previousNode[PARENT] = context; + + // remove empty nodes + if ( + options.removeEmpty && + previousNode != null && + previousNode.chi!.length == 0 && + context.chi![context.chi!.length - 1] == previousNode + ) { + context.chi!.pop(); + continue; + } + + break; + } + if (options.visitor != null) { let parens: Token[] | null; for (const result of walk(ast)) { @@ -2518,24 +2540,6 @@ export async function doParse( } } - while (stack.length > 0 && context != ast) { - const previousNode: AstAtRule | AstRule = stack.pop() as AstAtRule | AstRule; - context = (stack[stack.length - 1] ?? ast) as AstRuleList; - - // remove empty nodes - if ( - options.removeEmpty && - previousNode != null && - previousNode.chi!.length == 0 && - context.chi![context.chi!.length - 1] == previousNode - ) { - context.chi!.pop(); - continue; - } - - break; - } - if (options.minify) { if (ast.chi.length > 0) { let passes: number = options.pass ?? (1 as number); diff --git a/src/lib/renderer/render.ts b/src/lib/renderer/render.ts index c87f1742..aed9f15a 100644 --- a/src/lib/renderer/render.ts +++ b/src/lib/renderer/render.ts @@ -421,7 +421,7 @@ function renderAstNode( // @ts-ignore let children: string = ""; let str: string = ""; - let previousStr: string = ""; + // let previousStr: string = ""; const indent: string = indents[level]; const indentSub: string = indents[level + 1]; @@ -468,6 +468,10 @@ function renderAstNode( } children += str; + + if (sourcemaps != null && str !== "" && options.newLine) { + move(sourceLocation, linesMap!, options.newLine as string); + } } return children; @@ -482,18 +486,21 @@ function renderAstNode( };`; } - const prelude = [EnumToken.AtRuleNodeType, EnumToken.KeyframesAtRuleNodeType].includes(data.typ) - ? `@${(data).nam}${(data).val === "" ? "" : options.indent || " "}${ - (data).val - }${options.indent}{` - : (data).sel + `${options.indent}{`; + const prelude = + (indent.length > 0 ? options.newLine : "") + + indent + + ([EnumToken.AtRuleNodeType, EnumToken.KeyframesAtRuleNodeType].includes(data.typ) + ? `@${(data).nam}${(data).val === "" ? "" : options.indent || " "}${ + (data).val + }${options.indent}{` + : (data).sel + `${options.indent}{`); if (sourcemaps != null) { updateSourceMap(data, options, cache, sourcemaps, sourceLocation, linesMap!, prelude); } let node: AstNode; - let k: number = (data as AstRule | AstAtRule).chi!.length - 1; + let recordDeclarationSourceMap: boolean = data.typ == EnumToken.AtRuleNodeType; for (let i = 0; i < (data as AstRule | AstAtRule).chi!.length; i++) { node = (data as AstRule | AstAtRule).chi![i]; if (node.typ == EnumToken.CommentNodeType) { @@ -519,25 +526,7 @@ function renderAstNode( ) .reduce(reducer, "") .trimEnd()};`; - - if (sourcemaps != null) { - if (previousStr.length > 0) { - move(sourceLocation, linesMap!, previousStr); - } - } - - previousStr = str === "" ? "" : options.newLine + indentSub + str; - } - // else if (node.typ == EnumToken.AtRuleNodeType && !("chi" in node)) { - // str = `${(node).val === "" ? "" : options.indent || " "}${(node).val};`; - // } - else { - if (sourcemaps != null) { - if (previousStr.length > 0) { - move(sourceLocation, linesMap!, previousStr); - } - } - + } else { str = renderAstNode( node, options, @@ -551,7 +540,13 @@ function renderAstNode( indents, ); - previousStr = ""; + if (str === "") { + continue; + } + + children += str; + str = ""; + continue; } if (str === "") { @@ -560,16 +555,38 @@ function renderAstNode( str = options.newLine + indentSub + str; children += str; - } - if (sourcemaps != null && str !== "") { - move(sourceLocation, linesMap!, str.endsWith(";") ? str.slice(0, -1) : str); + if (sourcemaps != null && str !== "") { + move(sourceLocation, linesMap!, str); + + if (node.typ == EnumToken.DeclarationNodeType && recordDeclarationSourceMap) { + // if declaration is child of at-rule, then record it + // .rule { + // @media screen { + // color: red; + // } + // } + const source = options.sourcesMap!.get(node[LOC]!.srcId) as SourceFile; + sourcemaps.push([ + ...linesMap!.getOffsets( + sourceLocation.end - str.length + options.newLine!.length + indentSub.length, + ), + node[LOC]!.srcId, + ...source!.getOffsets(node[LOC].sta), + source.getFileName(), + source.getContent(), + ]); + } + } } if (children.endsWith(";")) { children = children.slice(0, -1); + sourceLocation.end--; } + if (options.removeEmpty && children === "") { + sourceLocation.end -= prelude.length; return ""; } diff --git a/test/specs/code/sourcemaps.js b/test/specs/code/sourcemaps.js index ec49c0ce..3080b219 100644 --- a/test/specs/code/sourcemaps.js +++ b/test/specs/code/sourcemaps.js @@ -1,44 +1,66 @@ import { ColorType, EnumToken, ModuleCaseTransformEnum, ModuleScopeEnumOptions } from "../../../dist/lib/ast/types.js"; -export function run(describe, expect, it, transform, parse, render, dirname, readFile, resolve) { +export function run( + describe, + expect, + it, + transform, + parse, + render, + dirname, + readFile, + resolve, + ColorType, + EnumToken, + ModuleCaseTransformEnum, + ModuleScopeEnumOptions, + transformSync, + parseSync, +) { + describe("sourcemap", function () { + const url = new URL(dirname(import.meta.url) + "/../../files/css/nested.css"); // const file = `@import '${dir}/files/css/line-awesome.css`; - describe('sourcemap', function () { - - - const url = new URL(dirname(import.meta.url) + '/../../css-modules/mixins.css'); // const file = `@import '${dir}/files/css/line-awesome.css`; const options = { input: ` - - .goal .bg-indigo { - background: indigo; - } - - - .indigo-white { - composes: bg-indigo; - composes: title block ruler from global; - color: white; - } - - .indigo-white { - composes: bg-indigo; - composes: button cell title from "${url.pathname}"; color: white; - } +@import '${url.pathname}'; +h1 { + text-transform: uppercase; +} +button { + background: linear-gradient( + if(media(min-width: 768px): to right; else: to bottom), + if(style(--dark-mode): #333; else: #fff), + if(style(--dark-mode): #000; else: #ccc) + ); +} `, - beautify: true, - sourcemap: 'inline', - module: ModuleScopeEnumOptions.ICSS, - output: 'test/sourcemap.html' + beautify: true, + sourcemap: "inline", + expandIfSyntax: true, + resolveImport: true, + output: "test/sourcemap.html", }; - - it('sourcemap file #1', async () => { - - return transform(options).then(async result => { - + + it("sourcemap unminified #1", async () => { + return transform(options).then(async (result) => { result.map.computePositions(); - const positions = result.map.find(11, 1); - return expect(positions.length == 1 && positions[0].slice(0, 3)).deep.equals([null, 3, 15]) + let positions = result.map.find(39, 3); + expect(positions?.length == 1 && positions[0].slice(0, 3)).deep.equals([null, 7, 3]); + }); + }); + + it("sourcemap minified #2", async () => { + return transform(options).then(async (result) => { + const result2 = transformSync({ + input: result.code, + sourcemap: "inline", + output: "test/sourcemap.html", + }); + + result2.map.computePositions(); + const positions = result2.map.find(1, 255); + expect(positions?.length == 1 && positions[0].slice(0, 3)).deep.equals([null, 23, 2]); }); }); }); -} \ No newline at end of file +} From ada7a3fb0e25a003ef5638f312bf4e96a47d6b14 Mon Sep 17 00:00:00 2001 From: Thierry Bela Nanga Date: Sun, 16 Aug 2026 21:52:48 -0400 Subject: [PATCH 05/22] bump version #146 --- README.md | 5 +- dist/index-umd-web.js | 46 +--- dist/index.cjs | 36 +-- dist/index.d.ts | 47 +--- dist/lib/renderer/render.js | 30 +-- dist/node.js | 6 +- dist/web.js | 16 +- files/getting-started.md | 2 +- files/index.md | 2 + files/minification.md | 4 +- files/plugins.md | 103 +++++++++ files/sourcemap.md | 2 +- files/syntax-lowering.md | 2 +- files/transform.md | 116 +--------- jsr.json | 2 +- package.json | 2 +- src/@types/ast.d.ts | 15 +- src/@types/index.d.ts | 15 ++ src/@types/token.d.ts | 343 +++++++++++++++++++++------- src/@types/validation.d.ts | 97 +++++--- src/@types/visitor.d.ts | 42 +++- src/@types/walker.d.ts | 36 +++ src/lib/ast/find.ts | 19 +- src/lib/ast/minify.ts | 76 +++--- src/lib/parser/parse.ts | 29 ++- src/lib/parser/utils/declaration.ts | 4 +- src/lib/parser/utils/selector.ts | 8 +- src/lib/parser/utils/token.ts | 5 +- src/lib/renderer/render.ts | 33 +-- src/lib/validation/match.ts | 82 +++++++ src/node.ts | 56 ++--- src/web.ts | 220 ++++++++++++++++-- 32 files changed, 963 insertions(+), 538 deletions(-) create mode 100644 files/plugins.md diff --git a/README.md b/README.md index a9b6e6af..ac470844 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ $ deno add @tbela99/css-parser * **`@import` flattening** to produce self-contained stylesheets. ## Vendor prefix removal -**Experimental vendor prefix cleanup** to modernize generated CSS. +**Vendor prefix cleanup** to modernize generated CSS. ## Syntax lowering CSS-Parser can transform these modern CSS features into lower-level CSS syntax: @@ -85,7 +85,8 @@ Try it [online](https://tbela99.github.io/css-parser/playground/) - [CSS Modules](https://tbela99.github.io/css-parser/docs/documents/Guide.CSS_Modules.html) - [Minification](https://tbela99.github.io/css-parser/docs/documents/Guide.Minification.html) - [Custom Transform](https://tbela99.github.io/css-parser/docs/documents/Guide.Custom_Transform.html) -- [Sourcema](https://tbela99.github.io/css-parser/docs/documents/Guide.Sourcemap.html) +- [Sourcemap](https://tbela99.github.io/css-parser/docs/documents/Guide.Sourcemap.html) +- [Plugins API](https://tbela99.github.io/css-parser/docs/documents/Guide.Plugins_API.html) - [Syntax Lowering](https://tbela99.github.io/css-parser/docs/documents/Guide.Syntax_Lowering.html) - [Ast Manipulation](https://tbela99.github.io/css-parser/docs/documents/Guide.Ast_Manipulation.html) - [Utility Functions](https://tbela99.github.io/css-parser/docs/documents/Guide.Utility_Functions.html) diff --git a/dist/index-umd-web.js b/dist/index-umd-web.js index 7c67416e..5171a65e 100644 --- a/dist/index-umd-web.js +++ b/dist/index-umd-web.js @@ -24827,7 +24827,6 @@ // @ts-ignore let children = ""; let str = ""; - // let previousStr: string = ""; const indent = indents[level]; const indentSub = indents[level + 1]; switch (data.typ) { @@ -24838,7 +24837,7 @@ case exports.EnumToken.CommentNodeType: case exports.EnumToken.CDOCOMMNodeType: if (data.val.startsWith("/*# sourceMappingURL=")) { - // ignore sourcemap + // ignore sourcemap comment return ""; } return !options.removeComments || (options.preserveLicense && data.val.startsWith("/*!")) @@ -24886,15 +24885,6 @@ : node.val; } else if (node.typ == exports.EnumToken.DeclarationNodeType) { - // if (!(node).nam.startsWith("--") && (node).val.length === 0) { - // // @ts-ignore - // errors.push({ - // action: "ignore", - // message: `render: invalid declaration ${JSON.stringify(node)}`, - // location: node[LOC], - // }); - // return ""; - // } str = `${node.nam}:${options.indent}${(options.minify ? filterValues(node.val) : node.val) @@ -24948,24 +24938,6 @@ move(sourceLocation, linesMap, end); } return prelude + children + end; - // case EnumToken.CssVariableTokenType: - // case EnumToken.CssVariableImportTokenType: - // return `@value ${(data).val}:${options.indent}${filterValues( - // options.minify - // ? (data).val - // : (data).val, - // ) - // .reduce(reducer, "") - // .trim()};`; - // case EnumToken.CssVariableDeclarationMapTokenType: - // return `@value ${filterValues((data as CssVariableMapTokenType).vars) - // .reduce((acc, curr) => acc + renderValue(curr), "") - // .trim()} from ${filterValues((data as CssVariableMapTokenType).from) - // .reduce((acc, curr) => acc + renderValue(curr), "") - // .trim()};`; - // case EnumToken.InvalidDeclarationNodeType: - // case EnumToken.InvalidRuleNodeType: - // case EnumToken.InvalidAtRuleNodeType: default: return ""; } @@ -32190,7 +32162,7 @@ * @throws Error file not found * * ```ts - * import {load, ResponseType} from '@tbela99/css-parser'; + * import {load, ResponseType} from '@tbela99/css-parser/web'; * const result = await load(file, '.', ResponseType.ArrayBuffer) as ArrayBuffer; * ``` */ @@ -32232,7 +32204,7 @@ * * ```ts * - * import {render, ColorType} from '@tbela99/css-parser'; + * import {render, ColorType} from '@tbela99/css-parser/web'; * * const css = 'body { color: color(from hsl(0 100% 50%) xyz x y z); }'; * const parseResult = await parse(css); @@ -32291,12 +32263,13 @@ /** * Parse css * @param args + * @private * * Parsing a string * * ```ts * - * import {parseSync} from '@tbela99/css-parser'; + * import {parseSync} from '@tbela99/css-parser/web'; * * // css string * let result = await parseSync(css, {nestingRules: true}); @@ -32346,11 +32319,11 @@ return !options.module && !options.inputSourceMap ? result : parseResult(result, options); } /** - * Transform css + * Transform CSS * * ```ts * - * import {transformSync} from '@tbela99/css-parser'; + * import {transformSync} from '@tbela99/css-parser/web'; * * // css string * const result = transformSync(css); @@ -32358,6 +32331,7 @@ * ``` * * @param args + * @private */ function transformSync(...args) { let options; @@ -32431,6 +32405,7 @@ * console.log(result.ast); * ``` * @param args + * @private */ async function parse(...args) { let options; @@ -32479,7 +32454,7 @@ return doParse(stream instanceof ReadableStream ? tokenizeStream(stream, options.parseInfo) : tokenize(options.parseInfo), options).then((result) => (!options.module && !options.inputSourceMap ? result : parseResult(result, options))); } /** - * Transform css file + * Transform CSS file * @param file url or path * @param options * @param asStream load file as stream @@ -32528,6 +32503,7 @@ * console.log(result.code); * ``` * @param args + * @private */ async function transform(...args) { let options; diff --git a/dist/index.cjs b/dist/index.cjs index 91dd92da..c60b7ab5 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -24830,7 +24830,6 @@ function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, erro // @ts-ignore let children = ""; let str = ""; - // let previousStr: string = ""; const indent = indents[level]; const indentSub = indents[level + 1]; switch (data.typ) { @@ -24841,7 +24840,7 @@ function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, erro case exports.EnumToken.CommentNodeType: case exports.EnumToken.CDOCOMMNodeType: if (data.val.startsWith("/*# sourceMappingURL=")) { - // ignore sourcemap + // ignore sourcemap comment return ""; } return !options.removeComments || (options.preserveLicense && data.val.startsWith("/*!")) @@ -24889,15 +24888,6 @@ function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, erro : node.val; } else if (node.typ == exports.EnumToken.DeclarationNodeType) { - // if (!(node).nam.startsWith("--") && (node).val.length === 0) { - // // @ts-ignore - // errors.push({ - // action: "ignore", - // message: `render: invalid declaration ${JSON.stringify(node)}`, - // location: node[LOC], - // }); - // return ""; - // } str = `${node.nam}:${options.indent}${(options.minify ? filterValues(node.val) : node.val) @@ -24951,24 +24941,6 @@ function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, erro move(sourceLocation, linesMap, end); } return prelude + children + end; - // case EnumToken.CssVariableTokenType: - // case EnumToken.CssVariableImportTokenType: - // return `@value ${(data).val}:${options.indent}${filterValues( - // options.minify - // ? (data).val - // : (data).val, - // ) - // .reduce(reducer, "") - // .trim()};`; - // case EnumToken.CssVariableDeclarationMapTokenType: - // return `@value ${filterValues((data as CssVariableMapTokenType).vars) - // .reduce((acc, curr) => acc + renderValue(curr), "") - // .trim()} from ${filterValues((data as CssVariableMapTokenType).from) - // .reduce((acc, curr) => acc + renderValue(curr), "") - // .trim()};`; - // case EnumToken.InvalidDeclarationNodeType: - // case EnumToken.InvalidRuleNodeType: - // case EnumToken.InvalidAtRuleNodeType: default: return ""; } @@ -32296,6 +32268,7 @@ const parseFile = node_util.deprecate(async (file, options = {}, asStream = fals /** * Parse css * @param args + * @private * * Parsing a string * @@ -32361,6 +32334,7 @@ function parseSync(...args) { * ``` * * @param args + * @private */ function transformSync(...args) { let options; @@ -32413,6 +32387,7 @@ function transformSync(...args) { * @param args * * @throws Error file not found + * @private * * Parsing a string * @@ -32498,7 +32473,7 @@ async function parse(...args) { return doParse(stream instanceof ReadableStream ? tokenizeStream(stream, options.parseInfo) : tokenize(options.parseInfo), options).then((result) => (!options.module && !options.inputSourceMap ? result : parseResult(result, options))); } /** - * Transform css file + * Transform CSS file * @param file url or path * @param options * @param asStream load file as stream @@ -32567,6 +32542,7 @@ const transformFile = node_util.deprecate(async (file, options = {}, asStream = * console.log(result.code); * ``` * @param args + * @private */ async function transform(...args) { let options; diff --git a/dist/index.d.ts b/dist/index.d.ts index 22524cb1..c1160d41 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -6205,12 +6205,10 @@ declare function transformSync(css: string, options?: TransformSyncOptions): Tra */ declare function transformSync(options: ParseInputOptions & TransformSyncOptions): TransformResult; /** - * Parse css + * Parse CSS * @param stream * @param options * - * @throws Error file not found - * * Example: * * ```ts @@ -6222,7 +6220,7 @@ declare function transformSync(options: ParseInputOptions & TransformSyncOptions * console.log(result.ast); * ``` * - * parsing a Readable stream + * parsing a ReadableStream * * ```ts * @@ -6237,7 +6235,7 @@ declare function transformSync(options: ParseInputOptions & TransformSyncOptions * console.log(result.ast); * ``` * - * Example using fetch and readable stream + * Parsing a file as a ReadableStream * * ```ts * @@ -6324,7 +6322,7 @@ declare function parse(options: ParseInputFileOptions & ParserOptions): Promise< */ declare function parse(options: ParseInputStreamOptions & ParserOptions): Promise; /** - * Transform css file + * Transform CSS file * @param file url or path * @param options * @param asStream load file as stream @@ -6349,7 +6347,7 @@ declare function parse(options: ParseInputStreamOptions & ParserOptions): Promis */ declare const transformFile: (file: string, options?: TransformOptions, asStream?: boolean) => Promise; /** - * Transform css + * Transform CSS * @param css * @param options * @@ -6422,7 +6420,7 @@ declare function transform(css: string | ReadableStream, options?: T * console.log(result.code); * ``` * - * Example using fetch + * Parse a file as a ReadableStream * * ```ts * @@ -6436,43 +6434,16 @@ declare function transform(css: string | ReadableStream, options?: T */ declare function transform(options: ParseInputStreamOptions & TransformOptions): Promise; /** - * Transform css + * Transform CSS * @param options * - * Parsing a string - * - * ```ts - * - * import {transform} from '@tbela99/css-parser'; - * - * // css string - * const result = await transform({input: css}); - * console.log(result.code); - * ``` - * - * Parsing a Readable stream + * Parsing a file * * ```ts * * import {transform} from '@tbela99/css-parser'; - * import {Readable} from "node:stream"; - * - * // usage: node index.ts < styles.css or cat styles.css | node index.ts - * - * const readableStream = Readable.toWeb(process.stdin); - * const result = await transform( {input: readableStream, beautify: true}); - * - * console.log(result.code); - * ``` - * - * Example using fetch - * - * ```ts - * - * import {transform} from '@tbela99/css-parser'; - * - * result = await transform({file: 'https://docs.deno.com/styles.css', beautify: true}); * + * const result = await transform( {file: 'https://docs.deno.com/styles.css', beautify: true}); * console.log(result.code); * ``` */ diff --git a/dist/lib/renderer/render.js b/dist/lib/renderer/render.js index f7710816..faeb33b9 100644 --- a/dist/lib/renderer/render.js +++ b/dist/lib/renderer/render.js @@ -254,7 +254,6 @@ function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, erro // @ts-ignore let children = ""; let str = ""; - // let previousStr: string = ""; const indent = indents[level]; const indentSub = indents[level + 1]; switch (data.typ) { @@ -265,7 +264,7 @@ function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, erro case EnumToken.CommentNodeType: case EnumToken.CDOCOMMNodeType: if (data.val.startsWith("/*# sourceMappingURL=")) { - // ignore sourcemap + // ignore sourcemap comment return ""; } return !options.removeComments || (options.preserveLicense && data.val.startsWith("/*!")) @@ -313,15 +312,6 @@ function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, erro : node.val; } else if (node.typ == EnumToken.DeclarationNodeType) { - // if (!(node).nam.startsWith("--") && (node).val.length === 0) { - // // @ts-ignore - // errors.push({ - // action: "ignore", - // message: `render: invalid declaration ${JSON.stringify(node)}`, - // location: node[LOC], - // }); - // return ""; - // } str = `${node.nam}:${options.indent}${(options.minify ? filterValues(node.val) : node.val) @@ -375,24 +365,6 @@ function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, erro move(sourceLocation, linesMap, end); } return prelude + children + end; - // case EnumToken.CssVariableTokenType: - // case EnumToken.CssVariableImportTokenType: - // return `@value ${(data).val}:${options.indent}${filterValues( - // options.minify - // ? (data).val - // : (data).val, - // ) - // .reduce(reducer, "") - // .trim()};`; - // case EnumToken.CssVariableDeclarationMapTokenType: - // return `@value ${filterValues((data as CssVariableMapTokenType).vars) - // .reduce((acc, curr) => acc + renderValue(curr), "") - // .trim()} from ${filterValues((data as CssVariableMapTokenType).from) - // .reduce((acc, curr) => acc + renderValue(curr), "") - // .trim()};`; - // case EnumToken.InvalidDeclarationNodeType: - // case EnumToken.InvalidRuleNodeType: - // case EnumToken.InvalidAtRuleNodeType: default: return ""; } diff --git a/dist/node.js b/dist/node.js index ca818d15..e30a1e25 100644 --- a/dist/node.js +++ b/dist/node.js @@ -137,6 +137,7 @@ const parseFile = deprecate(async (file, options = {}, asStream = false) => pars /** * Parse css * @param args + * @private * * Parsing a string * @@ -202,6 +203,7 @@ function parseSync(...args) { * ``` * * @param args + * @private */ function transformSync(...args) { let options; @@ -254,6 +256,7 @@ function transformSync(...args) { * @param args * * @throws Error file not found + * @private * * Parsing a string * @@ -339,7 +342,7 @@ async function parse(...args) { return doParse(stream instanceof ReadableStream ? tokenizeStream(stream, options.parseInfo) : tokenize(options.parseInfo), options).then((result) => (!options.module && !options.inputSourceMap ? result : parseResult(result, options))); } /** - * Transform css file + * Transform CSS file * @param file url or path * @param options * @param asStream load file as stream @@ -408,6 +411,7 @@ const transformFile = deprecate(async (file, options = {}, asStream = false) => * console.log(result.code); * ``` * @param args + * @private */ async function transform(...args) { let options; diff --git a/dist/web.js b/dist/web.js index f0ce3778..68753355 100644 --- a/dist/web.js +++ b/dist/web.js @@ -28,7 +28,7 @@ export { FeatureWalkMode } from './lib/ast/features/type.js'; * @throws Error file not found * * ```ts - * import {load, ResponseType} from '@tbela99/css-parser'; + * import {load, ResponseType} from '@tbela99/css-parser/web'; * const result = await load(file, '.', ResponseType.ArrayBuffer) as ArrayBuffer; * ``` */ @@ -70,7 +70,7 @@ async function load(url, currentDirectory = ".", responseType = false) { * * ```ts * - * import {render, ColorType} from '@tbela99/css-parser'; + * import {render, ColorType} from '@tbela99/css-parser/web'; * * const css = 'body { color: color(from hsl(0 100% 50%) xyz x y z); }'; * const parseResult = await parse(css); @@ -129,12 +129,13 @@ async function parseFile(file, options = {}, asStream = false) { /** * Parse css * @param args + * @private * * Parsing a string * * ```ts * - * import {parseSync} from '@tbela99/css-parser'; + * import {parseSync} from '@tbela99/css-parser/web'; * * // css string * let result = await parseSync(css, {nestingRules: true}); @@ -184,11 +185,11 @@ function parseSync(...args) { return !options.module && !options.inputSourceMap ? result : parseResult(result, options); } /** - * Transform css + * Transform CSS * * ```ts * - * import {transformSync} from '@tbela99/css-parser'; + * import {transformSync} from '@tbela99/css-parser/web'; * * // css string * const result = transformSync(css); @@ -196,6 +197,7 @@ function parseSync(...args) { * ``` * * @param args + * @private */ function transformSync(...args) { let options; @@ -269,6 +271,7 @@ function transformSync(...args) { * console.log(result.ast); * ``` * @param args + * @private */ async function parse(...args) { let options; @@ -317,7 +320,7 @@ async function parse(...args) { return doParse(stream instanceof ReadableStream ? tokenizeStream(stream, options.parseInfo) : tokenize(options.parseInfo), options).then((result) => (!options.module && !options.inputSourceMap ? result : parseResult(result, options))); } /** - * Transform css file + * Transform CSS file * @param file url or path * @param options * @param asStream load file as stream @@ -366,6 +369,7 @@ async function transformFile(file, options = {}, asStream = false) { * console.log(result.code); * ``` * @param args + * @private */ async function transform(...args) { let options; diff --git a/files/getting-started.md b/files/getting-started.md index a1702631..1aafb051 100644 --- a/files/getting-started.md +++ b/files/getting-started.md @@ -40,7 +40,7 @@ A non-exhaustive list of features is provided below: * **CSS variable inlining** where values can be safely resolved. * **Duplicate declaration removal** to eliminate redundant rules. * **`@import` flattening** to produce self-contained stylesheets. -* **Experimental vendor prefix cleanup** to modernize generated CSS. +* **Vendor prefix cleanup** to modernize generated CSS. ## Installation diff --git a/files/index.md b/files/index.md index 3bd1279c..91b6094b 100644 --- a/files/index.md +++ b/files/index.md @@ -10,6 +10,7 @@ children: - ./minification.md - ./transform.md - ./sourcemap.md + - ./plugins.md - ./syntax-lowering.md - ./ast.md - ./utilities.md @@ -24,6 +25,7 @@ children: - [Minification](./minification.md) - [Custom Transform](./transform.md) - [Sourcemap](./sourcemap.md) +- [Plugins API](./plugins.md) - [Syntax Lowering](./syntax-lowering.md) - [Ast Manipulation](./ast.md) - [Utility Functions](./utilities.md) diff --git a/files/minification.md b/files/minification.md index 4d329fac..56a418c6 100644 --- a/files/minification.md +++ b/files/minification.md @@ -657,7 +657,7 @@ Output: } ``` -### CSS prefix removal (Experimental) +### CSS prefix removal This feature is disabled by default. @@ -891,7 +891,7 @@ Output: ### Computed shorthands properties -Below is the list of computed shorthands properties. Minification is fully supported for the propertie with a checkmark. +Below is the list of computed shorthands properties. Minification is fully supported for the properties with a checkmark. - [ ] ~all~ - [x] animation diff --git a/files/plugins.md b/files/plugins.md new file mode 100644 index 00000000..6a003d31 --- /dev/null +++ b/files/plugins.md @@ -0,0 +1,103 @@ +--- +title: Plugins API +group: Documents +category: Guides +--- + +# Plugins + +The CSS parser supports plugin-style extensions through its [visitor API](./transform.md). + +### Example + +A plugin implemented as visitor that inlines all images under a specific size. + +```ts +import { + EnumToken, + FunctionURLToken, + load, + StringToken, + Token, + transform, + UrlToken, + ResponseType, + AstDeclaration, + AstNode +} from "@tbela99/css-parser"; + +function toBase64(arraybuffer: Uint8Array) { + // @ts-ignore + if (typeof Uint8Array.prototype.toBase64! == "function") { + // @ts-ignore + return arraybuffer.toBase64(); + } + + let binary = ""; + for (const byte of arraybuffer) { + binary += String.fromCharCode(byte); + } + + return btoa(binary); +} + +function inlineImagesPlugin(maxSize: number, extensions: string[]) { + return async function (node: FunctionURLToken, parent: AstNode) { + if (parent.typ == EnumToken.DeclarationNodeType) { + const t = node.chi.find( + (t) => t.typ != EnumToken.WhitespaceTokenType && t.typ != EnumToken.CommaTokenType, + ) as Token; + + if (t == null) { + return; + } + + const url = t.typ == EnumToken.StringTokenType ? (t as StringToken).val.slice(1, -1) : (t as UrlToken).val; + + if (url.startsWith("data:")) { + return; + } + + const matches = /(.*?\/)?([^/.]+)\.([^?#]+)([?#].*)?$/.exec(url); + + if (matches == null || !extensions.includes(matches[3].toLowerCase())) { + return; + } + + const buffer = (await load(url, ".", ResponseType.ArrayBuffer)) as ArrayBuffer; + + if (buffer.byteLength > maxSize) { + return; + } + + // change node type to EnumToken.String + Object.assign(t, { + typ: EnumToken.StringTokenType, + val: `"data:image/${matches[3].toLowerCase()};base64,${toBase64(new Uint8Array(buffer))}"`, + }); + } + }; +} + +// 35 kb or something +const maxSize = 35 * 1024; +// accepted images +const extensions = ["jpg", "gif", "png", "webp"]; +const css = ` +.goal .bg-indigo { + background: url(/img/animatecss-opengraph.jpg); +} +`; + +const result = await transform(css, { + visitor: { + UrlFunctionTokenType: inlineImagesPlugin(maxSize, extensions), + }, +}); + +console.error(result.code); +// .goal .bg-indigo{background:url("data:image/jpg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/4QugRXhpZgAA ...")} +``` + +------ +[← Sourcemap](./sourcemap.md) | [Syntax Lowering →](./syntax-lowering.md) \ No newline at end of file diff --git a/files/sourcemap.md b/files/sourcemap.md index 157f55a1..c8568696 100644 --- a/files/sourcemap.md +++ b/files/sourcemap.md @@ -108,4 +108,4 @@ console.log(result.map.toJSON()); ``` ------ -[← Custom Transform](./transform.md) | [Syntax Lowering →](./syntax-lowering.md) \ No newline at end of file +[← Custom Transform](./transform.md) | [Plugins API →](./plugins.md) \ No newline at end of file diff --git a/files/syntax-lowering.md b/files/syntax-lowering.md index f070397f..e13fe447 100644 --- a/files/syntax-lowering.md +++ b/files/syntax-lowering.md @@ -135,4 +135,4 @@ table.colortable th { ```` ------ -[← Custom Transform](./sourcemap.md) | [Ast Manipulation →](./ast.md) \ No newline at end of file +[← Plugins API](./plugins.md) | [Ast Manipulation →](./ast.md) \ No newline at end of file diff --git a/files/transform.md b/files/transform.md index 92b10cc5..cd3f7471 100644 --- a/files/transform.md +++ b/files/transform.md @@ -6,32 +6,10 @@ category: Guides ## Custom transform -Visitors are used to transform the ast tree produced by the parser. For more information about the visitor object see the [typescript definition](../docs/interfaces/node.VisitorNodeMap.html) - -## Plugin support through the visitor API - -The CSS parser supports plugin-style extensions through its visitor API. You can register handlers for specific AST node types and lifecycle events such as enter, visit, and leave to inspect, validate, or modify nodes without altering the parser internals. +Visitors are used to transform the ast tree produced by the parser. For more information about the visitor object see the [typescript definition](../docs/interfaces/node.VisitorNodeMap.html). You can register handlers for specific AST node types and lifecycle events such as enter, visit, and leave to inspect, validate, or modify nodes without altering the parser internals. This pattern is useful for building reusable plugins that enforce conventions, inject transformations, or add custom analysis on top of the parsed AST. -```ts -import {transform, type ParserOptions} from '@tbela99/css-parser'; - -const options: ParserOptions = { - visitor: { - Rule: { - '.card': (node) => { - node.selector = '.card, .panel'; - return node; - } - } - } -}; - -const result = await transform('.card { color: red; }', options); -console.log(result.code); -``` - ## Visitors execution order Visitors can be called when the node is entered, visited or left. @@ -445,97 +423,5 @@ console.debug(await transform(css, options)); // body {color:#f3fff0} ``` - -### Example of plugin - -A plugin implemented as visitor that inlines all images under a specific size. - -```ts -import { - EnumToken, - FunctionURLToken, - load, - StringToken, - Token, - transform, - UrlToken, - ResponseType, - AstDeclaration, - AstNode -} from "@tbela99/css-parser"; - -function toBase64(arraybuffer: Uint8Array) { - // @ts-ignore - if (typeof Uint8Array.prototype.toBase64! == "function") { - // @ts-ignore - return arraybuffer.toBase64(); - } - - let binary = ""; - for (const byte of arraybuffer) { - binary += String.fromCharCode(byte); - } - - return btoa(binary); -} - -function inlineImagesPlugin(maxSize: number, extensions: string[]) { - return async function (node: FunctionURLToken, parent: AstNode) { - if (parent.typ == EnumToken.DeclarationNodeType) { - const t = node.chi.find( - (t) => t.typ != EnumToken.WhitespaceTokenType && t.typ != EnumToken.CommaTokenType, - ) as Token; - - if (t == null) { - return; - } - - const url = t.typ == EnumToken.StringTokenType ? (t as StringToken).val.slice(1, -1) : (t as UrlToken).val; - - if (url.startsWith("data:")) { - return; - } - - const matches = /(.*?\/)?([^/.]+)\.([^?#]+)([?#].*)?$/.exec(url); - - if (matches == null || !extensions.includes(matches[3].toLowerCase())) { - return; - } - - const buffer = (await load(url, ".", ResponseType.ArrayBuffer)) as ArrayBuffer; - - if (buffer.byteLength > maxSize) { - return; - } - - // change node type to EnumToken.String - Object.assign(t, { - typ: EnumToken.StringTokenType, - val: `"data:image/${matches[3].toLowerCase()};base64,${toBase64(new Uint8Array(buffer))}"`, - }); - } - }; -} - -// 35 kb or something -const maxSize = 35 * 1024; -// accepted images -const extensions = ["jpg", "gif", "png", "webp"]; -const css = ` -.goal .bg-indigo { - background: url(/img/animatecss-opengraph.jpg); -} -`; - -const result = await transform(css, { - visitor: { - UrlFunctionTokenType: inlineImagesPlugin(maxSize, extensions), - }, -}); - -console.error(result.code); -// .goal .bg-indigo{background:url("data:image/jpg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/4QugRXhpZgAA ...")} -``` - ------ [← Minification](./minification.md) | [Sourcemap →](./sourcemap.md) \ No newline at end of file diff --git a/jsr.json b/jsr.json index 3cf6e541..2828ebed 100644 --- a/jsr.json +++ b/jsr.json @@ -1,6 +1,6 @@ { "name": "@tbela99/css-parser", - "version": "1.5.0-alpha.1", + "version": "1.5.0", "publish": { "include": [ "src", diff --git a/package.json b/package.json index 165717ee..3f0014e3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@tbela99/css-parser", "description": "CSS parser, minifier and validator for node and the browser", - "version": "1.5.0-alpha.1", + "version": "1.5.0", "exports": { ".": "./dist/node.js", "./node": "./dist/node.js", diff --git a/src/@types/ast.d.ts b/src/@types/ast.d.ts index bdc2b2e5..5a577a90 100644 --- a/src/@types/ast.d.ts +++ b/src/@types/ast.d.ts @@ -1,6 +1,7 @@ -import {EnumToken} from "../lib/ast/types.ts"; -import {ERRORS, LOC, OPTIMIZED, PARENT, RAW, ROOT, STATE, TOKENS} from "../lib/syntax/constants.ts"; -import type {Token} from "./token.d.ts"; +import { EnumToken } from "../lib/ast/types.ts"; +import { ERRORS, LOC, OPTIMIZED, PARENT, RAW, ROOT, STATE, TOKENS } from "../lib/syntax/constants.ts"; +import type { Token } from "./token.d.ts"; +import type { AstNode } from "./ast.d.ts"; /** * token or node location @@ -75,7 +76,7 @@ export declare interface BaseToken { /** * parent node */ - parent?: AstAtRule | astRule | AstKeyframesAtRule | AstKeyFrameRule | AstInvalidRule | AstInvalidAtRule | null; + parent?: AstAtRule | astRule | AstKeyframesAtRule | AstKeyframesRule | AstInvalidRule | AstInvalidAtRule | null; /** * @private */ @@ -220,7 +221,7 @@ export declare interface AstInvalidAtRule extends BaseToken, AstNodeStatus { /** * keyframe rule node */ -export declare interface AstKeyFrameRule extends BaseToken, AstNodeStatus { +export declare interface AstKeyframesRule extends BaseToken, AstNodeStatus { /** * token type */ @@ -378,7 +379,7 @@ export declare type AstRuleList = | AstAtRule | AstRule | AstKeyframesAtRule - | AstKeyFrameRule + | AstKeyframesRule | AstInvalidRule; /** @@ -406,7 +407,7 @@ export declare type AstNode = | AstRule | AstDeclaration | AstKeyframesAtRule - | AstKeyFrameRule + | AstKeyframesRule | AstInvalidRule | AstInvalidAtRule | AstInvalidDeclaration diff --git a/src/@types/index.d.ts b/src/@types/index.d.ts index d32373ff..70fcdea3 100644 --- a/src/@types/index.d.ts +++ b/src/@types/index.d.ts @@ -7,6 +7,7 @@ import type { CssVariableToken, Token } from "./token.d.ts"; import { FeatureWalkMode } from "../lib/ast/features/type.ts"; import { ValidationToken } from "../lib/validation/parser/types"; import { SourceFile } from "../lib/parser/source.ts"; +import type { VisitorSyncNodeMap, VisitorNodeMap } from "./visitor.d.ts"; export * from "./ast.d.ts"; export * from "./token.d.ts"; @@ -447,10 +448,21 @@ export declare interface ParseInputStreamOptions { * @internal */ export declare interface ParseSourceOptions { + /** + * Source file to be used for sourcemap + * @internal + */ sourcesMap?: Map; + /** + * Source file to be used for sourcemap + * @internal + */ source?: SourceFile | null; } +/** + * Parser sourcemap options + */ export declare interface ParserSourceMapOptions { /** * Include sourcemap in the ast. Sourcemap info is always generated @@ -462,6 +474,9 @@ export declare interface ParserSourceMapOptions { inputSourceMap?: SourceMapObject | string; } +/** + * Sync parseroptions + */ export declare interface ParserSyncOptions extends MinifyOptions, diff --git a/src/@types/token.d.ts b/src/@types/token.d.ts index 1c800e3e..667ebd2e 100644 --- a/src/@types/token.d.ts +++ b/src/@types/token.d.ts @@ -6,7 +6,7 @@ import { ColorType, EnumToken, EnumAstNodeStatus } from "../lib/ast/types.ts"; */ export declare interface LiteralToken extends BaseToken { /** - * literal type + * @inheritdoc */ typ: EnumToken.LiteralTokenType; /** @@ -20,7 +20,7 @@ export declare interface LiteralToken extends BaseToken { */ export declare interface ClassSelectorToken extends BaseToken { /** - * class selector type + * @inheritdoc */ typ: EnumToken.ClassSelectorTokenType; /** @@ -34,7 +34,7 @@ export declare interface ClassSelectorToken extends BaseToken { */ export declare interface InvalidClassSelectorToken extends BaseToken { /** - * invalid class selector type + * @inheritdoc */ typ: EnumToken.InvalidClassSelectorTokenType; /** @@ -48,7 +48,7 @@ export declare interface InvalidClassSelectorToken extends BaseToken { */ export declare interface UniversalSelectorToken extends BaseToken { /** - * universal selector type + * @inheritdoc */ typ: EnumToken.UniversalSelectorTokenType; } @@ -58,7 +58,7 @@ export declare interface UniversalSelectorToken extends BaseToken { */ export declare interface IdentToken extends BaseToken { /** - * ident type + * @inheritdoc */ typ: EnumToken.IdenTokenType; /** @@ -72,7 +72,7 @@ export declare interface IdentToken extends BaseToken { */ export declare interface IdentListToken extends BaseToken { /** - * ident list type + * @inheritdoc */ typ: EnumToken.IdenListTokenType; /** @@ -86,7 +86,7 @@ export declare interface IdentListToken extends BaseToken { */ export declare interface DashedIdentToken extends BaseToken { /** - * ident type + * @inheritdoc */ typ: EnumToken.DashedIdenTokenType; /** @@ -100,7 +100,7 @@ export declare interface DashedIdentToken extends BaseToken { */ export declare interface CommaToken extends BaseToken { /** - * comma type + * @inheritdoc */ typ: EnumToken.CommaTokenType; } @@ -110,7 +110,7 @@ export declare interface CommaToken extends BaseToken { */ export declare interface ColonToken extends BaseToken { /** - * colon type ':' + * @inheritdoc */ typ: EnumToken.ColonTokenType; } @@ -120,7 +120,7 @@ export declare interface ColonToken extends BaseToken { */ export declare interface DoubleColonToken extends BaseToken { /** - * double colon type '::' + * @inheritdoc */ typ: EnumToken.DoubleColonTokenType; } @@ -130,7 +130,7 @@ export declare interface DoubleColonToken extends BaseToken { */ export declare interface SemiColonToken extends BaseToken { /** - * semicolon type + * @inheritdoc */ typ: EnumToken.SemiColonTokenType; } @@ -140,7 +140,7 @@ export declare interface SemiColonToken extends BaseToken { */ export declare interface NestingSelectorToken extends BaseToken { /** - * nesting selector type + * @inheritdoc */ typ: EnumToken.NestingSelectorTokenType; } @@ -150,7 +150,7 @@ export declare interface NestingSelectorToken extends BaseToken { */ export declare interface NumberToken extends BaseToken { /** - * number type + * @inheritdoc */ typ: EnumToken.NumberTokenType; /** @@ -168,7 +168,7 @@ export declare interface NumberToken extends BaseToken { */ export declare interface AtRuleToken extends BaseToken { /** - * at rule type + * @inheritdoc */ typ: EnumToken.AtRuleTokenType; /** @@ -186,7 +186,7 @@ export declare interface AtRuleToken extends BaseToken { */ export declare interface PercentageToken extends BaseToken { /** - * percentage type + * @inheritdoc */ typ: EnumToken.PercentageTokenType; /** @@ -200,7 +200,7 @@ export declare interface PercentageToken extends BaseToken { */ export declare interface FlexToken extends BaseToken { /** - * flex type + * @inheritdoc */ typ: EnumToken.FlexTokenType; /** @@ -242,7 +242,7 @@ export declare interface FunctionToken extends BaseToken { */ export declare interface GridTemplateFuncToken extends BaseToken { /** - * function type + * @inheritdoc */ typ: EnumToken.GridTemplateFuncTokenType; /** @@ -260,7 +260,7 @@ export declare interface GridTemplateFuncToken extends BaseToken { */ export declare interface FunctionURLToken extends BaseToken { /** - * function type + * @inheritdoc */ typ: EnumToken.UrlFunctionTokenType; /** @@ -278,7 +278,7 @@ export declare interface FunctionURLToken extends BaseToken { */ export declare interface FunctionImageToken extends BaseToken { /** - * function type + * @inheritdoc */ typ: EnumToken.ImageFunctionTokenType; /** @@ -305,7 +305,7 @@ export declare interface FunctionImageToken extends BaseToken { */ export declare interface TimingFunctionToken extends BaseToken { /** - * timing function type + * @inheritdoc */ typ: EnumToken.TimingFunctionTokenType; /** @@ -323,7 +323,7 @@ export declare interface TimingFunctionToken extends BaseToken { */ export declare interface TimelineFunctionToken extends BaseToken { /** - * timeline function type + * @inheritdoc */ typ: EnumToken.TimelineFunctionTokenType; /** @@ -341,7 +341,7 @@ export declare interface TimelineFunctionToken extends BaseToken { */ export declare interface StringToken extends BaseToken { /** - * string type + * @inheritdoc */ typ: EnumToken.StringTokenType; /** @@ -355,7 +355,7 @@ export declare interface StringToken extends BaseToken { */ export declare interface BadStringToken extends BaseToken { /** - * bad string type + * @inheritdoc */ typ: EnumToken.BadStringTokenType; /** @@ -369,7 +369,7 @@ export declare interface BadStringToken extends BaseToken { */ export declare interface UnclosedStringToken extends BaseToken { /** - * unclosed string type + * @inheritdoc */ typ: EnumToken.UnclosedStringTokenType; /** @@ -383,7 +383,7 @@ export declare interface UnclosedStringToken extends BaseToken { */ export declare interface DimensionToken extends BaseToken { /** - * dimension type + * @inheritdoc */ typ: EnumToken.DimensionTokenType; /** @@ -401,7 +401,7 @@ export declare interface DimensionToken extends BaseToken { */ export declare interface LengthToken extends BaseToken { /** - * length type + * @inheritdoc */ typ: EnumToken.LengthTokenType; /** @@ -419,7 +419,7 @@ export declare interface LengthToken extends BaseToken { */ export declare interface AngleToken extends BaseToken { /** - * angle type + * @inheritdoc */ typ: EnumToken.AngleTokenType; /** @@ -437,7 +437,7 @@ export declare interface AngleToken extends BaseToken { */ export declare interface TimeToken extends BaseToken { /** - * time type + * @inheritdoc */ typ: EnumToken.TimeTokenType; /** @@ -445,7 +445,7 @@ export declare interface TimeToken extends BaseToken { */ val: number | FractionToken; /** - * time unit + * time unit */ unit: "ms" | "s"; } @@ -455,7 +455,7 @@ export declare interface TimeToken extends BaseToken { */ export declare interface FrequencyToken extends BaseToken { /** - * frequency type + * @inheritdoc */ typ: EnumToken.FrequencyTokenType; /** @@ -473,7 +473,7 @@ export declare interface FrequencyToken extends BaseToken { */ export declare interface ResolutionToken extends BaseToken { /** - * resolution type + * @inheritdoc */ typ: EnumToken.ResolutionTokenType; /** @@ -491,7 +491,7 @@ export declare interface ResolutionToken extends BaseToken { */ export declare interface HashToken extends BaseToken { /** - * hash type + * @inheritdoc */ typ: EnumToken.HashTokenType; /** @@ -505,7 +505,7 @@ export declare interface HashToken extends BaseToken { */ export declare interface BlockStartToken extends BaseToken { /** - * block start type + * @inheritdoc */ typ: EnumToken.BlockStartTokenType; } @@ -515,7 +515,7 @@ export declare interface BlockStartToken extends BaseToken { */ export declare interface BlockEndToken extends BaseToken { /** - * block end type + * @inheritdoc */ typ: EnumToken.BlockEndTokenType; } @@ -525,7 +525,7 @@ export declare interface BlockEndToken extends BaseToken { */ export declare interface AttrStartToken extends BaseToken { /** - * attribute start type + * @inheritdoc */ typ: EnumToken.AttrStartTokenType; /** @@ -539,7 +539,7 @@ export declare interface AttrStartToken extends BaseToken { */ export declare interface AttrEndToken extends BaseToken { /** - * attribute end type + * @inheritdoc */ typ: EnumToken.AttrEndTokenType; } @@ -549,7 +549,7 @@ export declare interface AttrEndToken extends BaseToken { */ export declare interface ParensStartToken extends BaseToken { /** - * parenthesis start type + * @inheritdoc */ typ: EnumToken.StartParensTokenType; } @@ -559,7 +559,7 @@ export declare interface ParensStartToken extends BaseToken { */ export declare interface ParensEndToken extends BaseToken { /** - * parenthesis end type + * @inheritdoc */ typ: EnumToken.EndParensTokenType; } @@ -569,7 +569,7 @@ export declare interface ParensEndToken extends BaseToken { */ export declare interface ParensToken extends BaseToken { /** - * parenthesis type + * @inheritdoc */ typ: EnumToken.ParensTokenType; /** @@ -583,7 +583,7 @@ export declare interface ParensToken extends BaseToken { */ export declare interface WhitespaceToken extends BaseToken { /** - * whitespace type + * @inheritdoc */ typ: EnumToken.WhitespaceTokenType; /** @@ -597,7 +597,7 @@ export declare interface WhitespaceToken extends BaseToken { */ export declare interface CommentToken extends BaseToken { /** - * comment type + * @inheritdoc */ typ: EnumToken.CommentTokenType; /** @@ -611,7 +611,7 @@ export declare interface CommentToken extends BaseToken { */ export declare interface BadCommentToken extends BaseToken { /** - * bad comment type + * @inheritdoc */ typ: EnumToken.BadCommentTokenType; /** @@ -624,7 +624,13 @@ export declare interface BadCommentToken extends BaseToken { * CDO comment token */ export declare interface CDOCommentToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.CDOCOMMTokenType; + /** + * CDO comment value + */ val: string; } @@ -633,7 +639,7 @@ export declare interface CDOCommentToken extends BaseToken { */ export declare interface BadCDOCommentToken extends BaseToken { /** - * bad CDO comment type + * @inheritdoc */ typ: EnumToken.BadCdoTokenType; /** @@ -647,7 +653,7 @@ export declare interface BadCDOCommentToken extends BaseToken { */ export declare interface IncludeMatchToken extends BaseToken { /** - * include match type + * @inheritdoc */ typ: EnumToken.IncludeMatchTokenType; // val: '~='; @@ -658,7 +664,7 @@ export declare interface IncludeMatchToken extends BaseToken { */ export declare interface DashMatchToken extends BaseToken { /** - * dash match type + * @inheritdoc */ typ: EnumToken.DashMatchTokenType; // val: '|='; @@ -669,7 +675,7 @@ export declare interface DashMatchToken extends BaseToken { */ export declare interface EqualMatchToken extends BaseToken { /** - * equal match type + * @inheritdoc */ typ: EnumToken.EqualMatchTokenType; // val: '|='; @@ -680,7 +686,7 @@ export declare interface EqualMatchToken extends BaseToken { */ export declare interface StartMatchToken extends BaseToken { /** - * start match type + * @inheritdoc */ typ: EnumToken.StartMatchTokenType; // val: '^='; @@ -691,7 +697,7 @@ export declare interface StartMatchToken extends BaseToken { */ export declare interface EndMatchToken extends BaseToken { /** - * end match type + * @inheritdoc */ typ: EnumToken.EndMatchTokenType; // val: '|='; @@ -702,7 +708,7 @@ export declare interface EndMatchToken extends BaseToken { */ export declare interface ContainMatchToken extends BaseToken { /** - * contain match type + * @inheritdoc */ typ: EnumToken.ContainMatchTokenType; // val: '|='; @@ -713,7 +719,7 @@ export declare interface ContainMatchToken extends BaseToken { */ export declare interface LessThanToken extends BaseToken { /** - * less than type + * @inheritdoc */ typ: EnumToken.LtTokenType; } @@ -723,7 +729,7 @@ export declare interface LessThanToken extends BaseToken { */ export declare interface LessThanOrEqualToken extends BaseToken { /** - * less than or equal type + * @inheritdoc */ typ: EnumToken.LteTokenType; } @@ -733,7 +739,7 @@ export declare interface LessThanOrEqualToken extends BaseToken { */ export declare interface GreaterThanToken extends BaseToken { /** - * greater than type + * @inheritdoc */ typ: EnumToken.GtTokenType; } @@ -743,7 +749,7 @@ export declare interface GreaterThanToken extends BaseToken { */ export declare interface GreaterThanOrEqualToken extends BaseToken { /** - * greater than or equal type + * @inheritdoc */ typ: EnumToken.GteTokenType; } @@ -753,7 +759,7 @@ export declare interface GreaterThanOrEqualToken extends BaseToken { */ export declare interface ColumnCombinatorToken extends BaseToken { /** - * column combinator type + * @inheritdoc */ typ: EnumToken.ColumnCombinatorTokenType; } @@ -763,7 +769,7 @@ export declare interface ColumnCombinatorToken extends BaseToken { */ export declare interface PseudoClassToken extends BaseToken { /** - * Pseudo class + * @inheritdoc */ typ: EnumToken.PseudoClassTokenType; /** @@ -777,7 +783,7 @@ export declare interface PseudoClassToken extends BaseToken { */ export declare interface PseudoElementToken extends BaseToken { /** - * Pseudo element + * @inheritdoc */ typ: EnumToken.PseudoElementTokenType; /** @@ -791,7 +797,7 @@ export declare interface PseudoElementToken extends BaseToken { */ export declare interface PseudoPageToken extends BaseToken { /** - * Pseudo page + * @inheritdoc */ typ: EnumToken.PseudoPageTokenType; /** @@ -805,7 +811,7 @@ export declare interface PseudoPageToken extends BaseToken { */ export declare interface PseudoClassFunctionToken extends BaseToken { /** - * Pseudo class function + * @inheritdoc */ typ: EnumToken.PseudoClassFuncTokenType; /** @@ -823,7 +829,7 @@ export declare interface PseudoClassFunctionToken extends BaseToken { */ export declare interface DelimToken extends BaseToken { /** - * Delimiter token type + * @inheritdoc */ typ: EnumToken.DelimTokenType; } @@ -833,7 +839,7 @@ export declare interface DelimToken extends BaseToken { */ export declare interface BadUrlToken extends BaseToken { /** - * Bad URL + * @inheritdoc */ typ: EnumToken.BadUrlTokenType; /** @@ -847,7 +853,7 @@ export declare interface BadUrlToken extends BaseToken { */ export declare interface UrlToken extends BaseToken { /** - * URL + * @inheritdoc */ typ: EnumToken.UrlTokenTokenType; /** @@ -861,7 +867,7 @@ export declare interface UrlToken extends BaseToken { */ export declare interface EOFToken extends BaseToken { /** - * End of file + * @inheritdoc */ typ: EnumToken.EOFTokenType; } @@ -871,7 +877,7 @@ export declare interface EOFToken extends BaseToken { */ export declare interface ImportantToken extends BaseToken { /** - * Important + * @inheritdoc */ typ: EnumToken.ImportantTokenType; } @@ -881,7 +887,7 @@ export declare interface ImportantToken extends BaseToken { */ export declare interface ColorToken extends BaseToken { /** - * Color type + * @inheritdoc */ typ: EnumToken.ColorTokenType; /** @@ -907,7 +913,7 @@ export declare interface ColorToken extends BaseToken { */ export declare interface AttrToken extends BaseToken { /** - * Attribute type + * @inheritdoc */ typ: EnumToken.AttrTokenType; /** @@ -921,7 +927,7 @@ export declare interface AttrToken extends BaseToken { */ export declare interface InvalidAttrToken extends BaseToken { /** - * Attribute type + * @inheritdoc */ typ: EnumToken.InvalidAttrTokenType; /** @@ -934,6 +940,9 @@ export declare interface InvalidAttrToken extends BaseToken { * Child combinator token */ export declare interface ChildCombinatorToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.ChildCombinatorTokenType; } @@ -942,7 +951,7 @@ export declare interface ChildCombinatorToken extends BaseToken { */ export declare interface MediaFeatureToken extends BaseToken { /** - * Media feature type + * @inheritdoc */ typ: EnumToken.MediaFeatureTokenType; /** @@ -956,7 +965,7 @@ export declare interface MediaFeatureToken extends BaseToken { */ export declare interface NotToken extends BaseToken { /** - * Media feature not type + * @inheritdoc */ typ: EnumToken.NotTokenType; /** @@ -970,7 +979,7 @@ export declare interface NotToken extends BaseToken { */ export declare interface MediaFeatureOnlyToken extends BaseToken { /** - * Media feature only type + * @inheritdoc */ typ: EnumToken.OnlyTokenType; /** @@ -984,7 +993,7 @@ export declare interface MediaFeatureOnlyToken extends BaseToken { */ export declare interface AndToken extends BaseToken { /** - * Media feature and type + * @inheritdoc */ typ: EnumToken.AndTokenType; } @@ -994,7 +1003,7 @@ export declare interface AndToken extends BaseToken { */ export declare interface OrToken extends BaseToken { /** - * Media feature or type + * @inheritdoc */ typ: EnumToken.OrTokenType; } @@ -1004,7 +1013,7 @@ export declare interface OrToken extends BaseToken { */ export declare interface MediaQueryUnaryFeatureToken extends BaseToken { /** - * Media query condition type + * @inheritdoc */ typ: EnumToken.MediaQueryUnaryFeatureTokenType; /** @@ -1019,7 +1028,7 @@ export declare interface MediaQueryUnaryFeatureToken extends BaseToken { export declare interface SupportsQueryUnaryConditionToken extends BaseToken { /** - * Supports query condition type + * @inheritdoc */ typ: EnumToken.SupportsQueryUnaryConditionTokenType; /** @@ -1034,7 +1043,7 @@ export declare interface SupportsQueryUnaryConditionToken extends BaseToken { export declare interface SupportsQueryConditionToken extends BaseToken { /** - * Supports query condition type + * @inheritdoc */ typ: EnumToken.SupportsQueryConditionTokenType; /** @@ -1053,7 +1062,7 @@ export declare interface SupportsQueryConditionToken extends BaseToken { export declare interface WhenElseQueryConditionToken extends BaseToken { /** - * When else query condition type + * @inheritdoc */ typ: EnumToken.WhenElseQueryConditionTokenType; /** @@ -1072,7 +1081,7 @@ export declare interface WhenElseQueryConditionToken extends BaseToken { export declare interface WhenElseUnaryConditionToken extends BaseToken { /** - * When else query condition type + * @inheritdoc */ typ: EnumToken.WhenElseUnaryConditionTokenType; /** @@ -1087,7 +1096,7 @@ export declare interface WhenElseUnaryConditionToken extends BaseToken { export declare interface MediaQueryConditionToken extends BaseToken { /** - * Media query condition type + * @inheritdoc */ typ: EnumToken.MediaQueryConditionTokenType; /** @@ -1114,7 +1123,7 @@ export declare interface MediaQueryConditionToken extends BaseToken { export declare interface IfConditionToken extends BaseToken { /** - * If condition type + * @inheritdoc */ typ: EnumToken.IfConditionTokenType; /** @@ -1129,7 +1138,7 @@ export declare interface IfConditionToken extends BaseToken { export declare interface IfElseConditionToken extends BaseToken { /** - * If else condition type + * @inheritdoc */ typ: EnumToken.IfElseConditionTokenType; /** @@ -1143,23 +1152,67 @@ export declare interface IfElseConditionToken extends BaseToken { } export declare interface ContainerStyleRangeToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.ContainerStyleRangeTokenType; + /** + * condition left handle + */ l: Token[]; + /** + * condition operator + */ op: Token[]; + /** + * condition value + */ r: Token[]; } +// (20px <= width < 30px) +/** + * @inheritdoc + */ export declare interface MediaRangeQueryToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.MediaRangeQueryTokenType; + /** + * left hanle + * */ l: Token[]; + /** + * media feature name + */ val: Token[]; + /** + * first comparator + */ op1: LessThanToken | GreaterThanToken | LessThanOrEqualToken | GreaterThanOrEqualToken; + /** + * second comparator + */ op2: LessThanToken | GreaterThanToken | LessThanOrEqualToken | GreaterThanOrEqualToken; + /** + * right handle + */ r: Token[]; } +/** + * @inheritdoc + */ export declare interface InvalidMediaQueryToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.InvalidMediaQueryTokenType; + + /** + * children + */ chi: Token[]; } @@ -1167,6 +1220,9 @@ export declare interface InvalidMediaQueryToken extends BaseToken { * Descendant combinator token */ export declare interface DescendantCombinatorToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.DescendantCombinatorTokenType; } @@ -1174,6 +1230,9 @@ export declare interface DescendantCombinatorToken extends BaseToken { * Next sibling combinator token */ export declare interface NextSiblingCombinatorToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.NextSiblingCombinatorTokenType; } @@ -1181,6 +1240,9 @@ export declare interface NextSiblingCombinatorToken extends BaseToken { * Subsequent sibling combinator token */ export declare interface SubsequentCombinatorToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.SubsequentSiblingCombinatorTokenType; } @@ -1188,6 +1250,9 @@ export declare interface SubsequentCombinatorToken extends BaseToken { * Add token */ export declare interface AddToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.Add; } @@ -1195,6 +1260,9 @@ export declare interface AddToken extends BaseToken { * Sub token */ export declare interface SubToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.Sub; } @@ -1202,6 +1270,9 @@ export declare interface SubToken extends BaseToken { * Div token */ export declare interface DivToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.Div; } @@ -1210,7 +1281,7 @@ export declare interface DivToken extends BaseToken { */ export declare interface MulToken extends BaseToken { /** - * Type + * @inheritdoc */ typ: EnumToken.Mul; } @@ -1220,7 +1291,7 @@ export declare interface MulToken extends BaseToken { */ export declare interface WrappedValuesToken extends BaseToken { /** - * Type + * @inheritdoc */ typ: EnumToken.WrappedValuesTokenType; /** @@ -1234,7 +1305,7 @@ export declare interface WrappedValuesToken extends BaseToken { */ export declare interface UnaryExpression extends BaseToken { /** - * Type + * @inheritdoc */ typ: EnumToken.UnaryExpressionTokenType; /** @@ -1251,8 +1322,17 @@ export declare interface UnaryExpression extends BaseToken { * Fraction token */ export declare interface FractionToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.FractionTokenType; + /** + * Left handle + */ l: NumberToken; + /** + * Right handle + */ r: NumberToken; } @@ -1260,9 +1340,21 @@ export declare interface FractionToken extends BaseToken { * Binary expression token */ export declare interface BinaryExpressionToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.BinaryExpressionTokenType; + /** + * Operator + */ op: EnumToken.Add | EnumToken.Sub | EnumToken.Div | EnumToken.Mul; + /** + * Left handle + */ l: BinaryExpressionNode | Token; + /** + * Right handle + */ r: BinaryExpressionNode | Token; } @@ -1270,10 +1362,25 @@ export declare interface BinaryExpressionToken extends BaseToken { * Match expression token */ export declare interface MatchExpressionToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.MatchExpressionTokenType; + /** + * Operator + */ op: EqualMatchToken | DashMatchToken | StartMatchToken | ContainMatchToken | EndMatchToken | IncludeMatchToken; + /** + * Left handle + */ l: Token; + /** + * Right handle + */ r: Token; + /** + * Flags + */ attr?: "i" | "s"; } @@ -1281,8 +1388,17 @@ export declare interface MatchExpressionToken extends BaseToken { * Name space attribute token */ export declare interface NameSpaceAttributeToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.NameSpaceAttributeTokenType; + /** + * Left handle + */ l?: Token; + /** + * Right handle + */ r: Token; } @@ -1290,7 +1406,13 @@ export declare interface NameSpaceAttributeToken extends BaseToken { * List token */ export declare interface ListToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.ListToken; + /** + * Children + */ chi: Token[]; } @@ -1298,8 +1420,17 @@ export declare interface ListToken extends BaseToken { * Composes selector token */ export declare interface ComposesSelectorToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.ComposesSelectorTokenType; + /** + * Left handle + */ l: Token[]; + /** + * Right handle + */ r: Token | null; } @@ -1307,20 +1438,53 @@ export declare interface ComposesSelectorToken extends BaseToken { * Css variable token */ export declare interface CssVariableToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.CssVariableTokenType; + /** + * Name + */ nam: string; + /** + * Value + */ val: Token[]; } +/** + * Css variable import token + */ export declare interface CssVariableImportTokenType extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.CssVariableImportTokenType; + /** + * Name + */ nam: string; + /** + * Value + */ val: Token[]; } +/** + * Css variable map token + */ export declare interface CssVariableMapTokenType extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.CssVariableDeclarationMapTokenType; + /** + * CSS Variables + */ vars: Token[]; + /** + * From clause + */ from: Token[]; } @@ -1328,6 +1492,9 @@ export declare interface CssVariableMapTokenType extends BaseToken { * Function definition token */ export declare interface FunctionDefToken extends BaseToken { + /** + * @inheritdoc + */ typ: | EnumToken.FunctionDefTokenType | EnumToken.UrlFunctionTokenDefType @@ -1339,7 +1506,13 @@ export declare interface FunctionDefToken extends BaseToken { | EnumToken.MathFunctionTokenDefType | EnumToken.PseudoClassFunctionTokenDefType | EnumToken.TransformFunctionTokenDefType; + /** + * Name + */ nam: string; + /** + * Value + */ val: string; } @@ -1347,7 +1520,13 @@ export declare interface FunctionDefToken extends BaseToken { * Raw node token */ export declare interface RawNodeToken extends BaseToken, EnumAstNodeStatus { + /** + * @inheritdoc + */ typ: EnumToken.RawNodeTokenType; + /** + * Value + */ val: Token[]; } diff --git a/src/@types/validation.d.ts b/src/@types/validation.d.ts index aa630143..b7b29f38 100644 --- a/src/@types/validation.d.ts +++ b/src/@types/validation.d.ts @@ -4,67 +4,106 @@ import type { Token } from "./token.d.ts"; import type { ValidationOptions } from "./index.d.ts"; import { MediaFeatureType, ValidationSyntaxGroupEnum } from "../lib/validation/parser/typedef.ts"; +/** + * Validation syntax + * @internal + */ export declare interface ValidationSyntaxNode { + /** + * mdn data syntax + */ syntax: string; + /** + * validation tokens + */ ast?: ValidationToken[]; + /** + * descriptors + */ descriptors?: Record>; } +/** + * Validation selector options + * @internal + */ export interface ValidationSelectorOptions extends ValidationOptions { + /** + * nested selector + */ nestedSelector?: boolean; } +/** + * Validation media feature + * @internal + */ export declare interface ValidationMediaFeature { + /** + * media feature type + */ type: MediaFeatureType; + /** + * media feature status + */ status?: string; + /** + * media feature category + */ category: string; + /** + * media feature values + */ values?: Array | Array; } +/** + * Validation configuration + * @internal + */ export declare type ValidationConfiguration = Record< ValidationSyntaxGroupEnum, ValidationSyntaxNode | Record | Record >; +/** + * Validation result + * @internal + */ export interface ValidationResult { + /** + * validation result + */ valid: SyntaxValidationResult; + /** + * node + */ node: AstNode | Token | null; + /** + * syntax + */ syntax: ValidationToken | string | null; + /** + * error + */ error: string; + /** + * cycle + */ cycle?: boolean; } +/** + * Validation syntax result + * @internal + */ export interface ValidationSyntaxResult extends ValidationResult { + /** + * syntax + */ syntax: ValidationToken | string | null; - context: Context | Token[]; -} - -export interface Context { - index: number; - /** - * The length of the context tokens to be consumed + * context */ - - readonly length: number; - - current(): Type | null; - - update(context: Context): void; - - consume(token: Type, howMany?: number): boolean; - - peek(): Type | null; - - // tokens(): Type[]; - - next(): Type | null; - - consume(token: Type, howMany?: number): boolean; - - slice(): Type[]; - - clone(): Context; - - done(): boolean; + context: ValidationContext | Token[]; } diff --git a/src/@types/visitor.d.ts b/src/@types/visitor.d.ts index 321a2450..8fc8d380 100644 --- a/src/@types/visitor.d.ts +++ b/src/@types/visitor.d.ts @@ -2,24 +2,40 @@ import type { AstAtRule, AstDeclaration, AstKeyframesAtRule, AstKeyframesRule, A import { WalkerEvent } from "../lib/ast/walk.ts"; import { EnumToken } from "../lib/ast/types.ts"; +/** + * Generic visitor result + */ export declare type GenericVisitorSyncResult = T | T[] | null; -export declare type GenericVisitorAsyncResult = Promise | Promise| Promise; +/** + * Generic visitor result + */ +export declare type GenericVisitorAsyncResult = Promise | Promise | Promise; +/** + * Generic visitor result + */ export declare type GenericVisitorResult = GenericVisitorSyncResult | GenericVisitorAsyncResult; - - +/** + * Generic visitor handler + */ export declare type GenericVisitorSyncHandler = ( node: T, parent?: AstNode | Token, root?: AstNode | Token, ) => GenericVisitorSyncResult; +/** + * Generic visitor handler + */ export declare type GenericVisitorAstNodeSyncHandlerMap = | Record> | GenericVisitorSyncHandler | { type: WalkerEvent; handler: GenericVisitorSyncHandler } | { type: WalkerEvent; handler: Record> }; +/** + * Generic visitor handler + */ export declare type ValueVisitorSyncHandler = GenericVisitorSyncHandler; /** @@ -229,8 +245,14 @@ export declare interface VisitorSyncNodeMap { */ Rule?: GenericVisitorAstNodeSyncHandlerMap; + /** + * keyframes rule visitor + */ KeyframesRule?: GenericVisitorAstNodeSyncHandlerMap; + /** + * keyframes at-rule visitor + */ KeyframesAtRule?: GenericVisitorAstNodeSyncHandlerMap; /** @@ -285,22 +307,32 @@ export declare interface VisitorSyncNodeMap { * // body {color:#f3fff0} * ``` */ - [key: keyof typeof EnumToken]: GenericVisitorAstNodeSyncHandlerMap | GenericVisitorAstNodeSyncHandlerMap; + [key: keyof typeof EnumToken]: + | GenericVisitorAstNodeSyncHandlerMap + | GenericVisitorAstNodeSyncHandlerMap; } - +/** + * Generic visitor handler + */ export declare type GenericVisitorHandler = ( node: T, parent?: AstNode | Token, root?: AstNode | Token, ) => GenericVisitorSyncResult | GenericVisitorAsyncResult; +/** + * Generic visitor handler + */ export declare type GenericVisitorAstNodeHandlerMap = | Record> | GenericVisitorHandler | { type: WalkerEvent; handler: GenericVisitorHandler } | { type: WalkerEvent; handler: Record> }; +/** + * Generic visitor handler + */ export declare type ValueVisitorHandler = GenericVisitorHandler; /** diff --git a/src/@types/walker.d.ts b/src/@types/walker.d.ts index 8eedabc6..c8868e69 100644 --- a/src/@types/walker.d.ts +++ b/src/@types/walker.d.ts @@ -33,18 +33,54 @@ export declare type WalkerValueFilter = ( parents?: Generator, ) => WalkerOption | null; +/** + * walker result + */ export declare interface WalkResult { + /** + * current node + */ node: AstNode; + /** + * parent node + */ parent?: AstRuleList; + /** + * root node + */ root?: AstNode; + /** + * parent nodes + */ parents: Generator; } +/** + * walker result + */ export declare interface WalkAttributesResult { + /** + * current node + */ value: Token; + /** + * previous node + */ previousValue: Token | null; + /** + * next node + */ nextValue: Token | null; + /** + * root node + */ root?: AstNode | Token | null; + /** + * parent node + */ parent: AstNode | Token | null; + /** + * parent nodes + */ parents: Generator; } diff --git a/src/lib/ast/find.ts b/src/lib/ast/find.ts index 89647f2a..c9933a11 100644 --- a/src/lib/ast/find.ts +++ b/src/lib/ast/find.ts @@ -1,8 +1,8 @@ -import type {Token} from "../../@types/token.d.ts"; -import type {AstDeclaration, AstNode, AstValueMatcher, TokenSearchResult} from "../../@types/ast.d.ts"; -import {EnumToken} from "./types.ts"; -import {walk, walkValues} from "./walk.ts"; -import {PARENT, TOKENS} from "../syntax/constants.ts"; +import type { Token } from "../../@types/token.d.ts"; +import type { AstDeclaration, AstNode, AstValueMatcher, TokenSearchResult } from "../../@types/ast.d.ts"; +import { EnumToken } from "./types.ts"; +import { walk, walkValues } from "./walk.ts"; +import { PARENT, TOKENS } from "../syntax/constants.ts"; /** * Search the ast subtree and return the first match @@ -46,6 +46,11 @@ export function find(ast: AstNode, matcher: (node: AstNode, parent?: AstNode | n } /** + * + * @param ast + * @param matcher + * @returns + * * Search the ast sub-tree by checking each node's value token and return the first match * ```ts @@ -71,10 +76,6 @@ button { console.log({node, value}); ``` - * - * @param ast - * @param matcher - * @returns */ export function findByValue( ast: AstNode, diff --git a/src/lib/ast/minify.ts b/src/lib/ast/minify.ts index f281a946..b5f85ae7 100644 --- a/src/lib/ast/minify.ts +++ b/src/lib/ast/minify.ts @@ -1,12 +1,12 @@ -import { eq } from "../parser/utils/eq.ts"; -import { doRender, renderValue } from "../renderer/render.ts"; +import {eq} from "../parser/utils/eq.ts"; +import {doRender, renderValue} from "../renderer/render.ts"; import * as allFeatures from "./features/index.ts"; -import { walkValues } from "./walk.ts"; +import {walkValues} from "./walk.ts"; import type { AstAtRule, AstDeclaration, - AstKeyFrameRule, AstKeyframesAtRule, + AstKeyframesRule, AstNode, AstRule, AstStyleSheet, @@ -24,15 +24,15 @@ import type { RawSelectorTokens, Token, } from "../../@types/index.d.ts"; -import { EnumToken } from "./types.ts"; -import { isFunction, isIdent, isIdentStart, isWhiteSpace } from "../syntax/syntax.ts"; -import { FeatureWalkMode } from "./features/type.ts"; -import { trimArray } from "../validation/match.ts"; -import { combinators, LOC, OPTIMIZED, PARENT, RAW, TOKENS } from "../syntax/constants.ts"; -import { replaceNodeOrValue } from "../parser/utils/token.ts"; -import { parseString } from "../parser/parse.ts"; -import { tokenize } from "../parser/tokenize.ts"; -import { replaceCompound } from "./expand.ts"; +import {EnumToken} from "./types.ts"; +import {isFunction, isIdent, isIdentStart, isWhiteSpace} from "../syntax/syntax.ts"; +import {FeatureWalkMode} from "./features/type.ts"; +import {trimArray} from "../validation/match.ts"; +import {combinators, LOC, OPTIMIZED, PARENT, RAW, TOKENS} from "../syntax/constants.ts"; +import {replaceNodeOrValue} from "../parser/utils/token.ts"; +import {parseString} from "../parser/parse.ts"; +import {tokenize} from "../parser/tokenize.ts"; +import {replaceCompound} from "./expand.ts"; const notEndingWith: string[] = ["(", "["].concat(combinators); const rules: EnumToken[] = [ @@ -77,7 +77,7 @@ export function minify( */ export function minify( ast: AstNode, - opt: ParserOptions | MinifyFeatureOptions = {}, + options: ParserOptions | MinifyFeatureOptions = {}, recursive: boolean = false, errors?: ErrorDescription[], nestingContent?: boolean, @@ -91,27 +91,27 @@ export function minify( let replacement: AstNode | null; // @ts-ignore - let { sourcemap, module, ...options } = opt; + let { sourcemap, module, ...options2 } = options; - if (!("features" in options)) { + if (!("features" in options2)) { // @ts-ignore - options = { + options2 = { removeDuplicateDeclarations: true, computeShorthand: true, computeCalcExpression: true, removePrefix: false, features: [], - ...options, + ...options2, }; for (const feature of features) { - feature.register(options); + feature.register(options2); } - options.features!.sort((a: MinifyFeature, b: MinifyFeature): number => a.ordering - b.ordering); + options2.features!.sort((a: MinifyFeature, b: MinifyFeature): number => a.ordering - b.ordering); } - for (const feature of options!.features as MinifyFeature[]) { + for (const feature of options2!.features as MinifyFeature[]) { if (feature.processMode & FeatureWalkMode.Pre) { preprocess = true; } @@ -131,7 +131,7 @@ export function minify( replacement = parent; - for (const feature of options.features as MinifyFeature[]) { + for (const feature of options2.features as MinifyFeature[]) { if ( (feature.processMode & FeatureWalkMode.Pre) === 0 || (feature.accept != null && !feature.accept.has(parent.typ)) @@ -149,7 +149,7 @@ export function minify( const result = feature.run( replacement, - options, + options2, parent[PARENT] ?? ast, context, FeatureWalkMode.Pre, @@ -178,15 +178,15 @@ export function minify( } } - for (const feature of options.features as MinifyFeature[]) { + for (const feature of options2.features as MinifyFeature[]) { if (feature.processMode & FeatureWalkMode.Pre && "cleanup" in feature) { // @ts-ignore - feature.cleanup(ast, options, context, FeatureWalkMode.Pre); + feature.cleanup(ast, options2, context, FeatureWalkMode.Pre); } } } - doMinify(ast, options, recursive, errors, nestingContent, context); + doMinify(ast, options2, recursive, errors, nestingContent, context); parents = new Set([ast]); @@ -198,7 +198,7 @@ export function minify( replacement = parent; if (postprocess) { - for (const feature of options.features as MinifyFeature[]) { + for (const feature of options2.features as MinifyFeature[]) { if ( (feature.processMode & FeatureWalkMode.Post) === 0 || (feature.accept != null && !feature.accept.has(parent.typ)) @@ -208,7 +208,7 @@ export function minify( const result = feature.run( replacement as AstRule | AstAtRule, - options, + options2, parent[PARENT] ?? (ast as AstRule | AstAtRule | AstStyleSheet), context, FeatureWalkMode.Post, @@ -239,10 +239,10 @@ export function minify( } if (postprocess) { - for (const feature of options.features as MinifyFeature[]) { + for (const feature of options2.features as MinifyFeature[]) { if (feature.processMode & FeatureWalkMode.Post && "cleanup" in feature) { // @ts-ignore - feature.cleanup(ast, options, context, FeatureWalkMode.Post); + feature.cleanup(ast, options2, context, FeatureWalkMode.Post); } } } @@ -523,11 +523,11 @@ function doMinify( } else if (node.typ === EnumToken.KeyFramesRuleNodeType) { if ( previous?.typ === EnumToken.KeyFramesRuleNodeType && - (node).sel === (previous).sel + (node).sel === (previous).sel ) { // do not merge keyframes // https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@keyframes#resolving_duplicates - (previous).chi.push(...(node).chi); + (previous).chi.push(...(node).chi); ast.chi.splice(i, 1); previous = (ast?.chi?.[nodeIndex] as AstNode) ?? null; @@ -538,22 +538,22 @@ function doMinify( let k: number; - for (k = 0; k < (node as AstKeyFrameRule).chi.length; k++) { - if ((node as AstKeyFrameRule).chi[k].typ == EnumToken.DeclarationNodeType) { - let l: number = ((node as AstKeyFrameRule).chi[k] as AstDeclaration).val.length; + for (k = 0; k < (node as AstKeyframesRule).chi.length; k++) { + if ((node as AstKeyframesRule).chi[k].typ == EnumToken.DeclarationNodeType) { + let l: number = ((node as AstKeyframesRule).chi[k] as AstDeclaration).val.length; while (l--) { if ( - ((node as AstKeyFrameRule).chi[k] as AstDeclaration).val[l].typ == + ((node as AstKeyframesRule).chi[k] as AstDeclaration).val[l].typ == EnumToken.ImportantTokenType ) { - (node as AstKeyFrameRule).chi.splice(k--, 1); + (node as AstKeyframesRule).chi.splice(k--, 1); break; } if ( [EnumToken.WhitespaceTokenType, EnumToken.CommentTokenType].includes( - ((node as AstKeyFrameRule).chi[k] as AstDeclaration).val[l].typ, + ((node as AstKeyframesRule).chi[k] as AstDeclaration).val[l].typ, ) ) { continue; diff --git a/src/lib/parser/parse.ts b/src/lib/parser/parse.ts index 3bfa7bad..719ad5fb 100644 --- a/src/lib/parser/parse.ts +++ b/src/lib/parser/parse.ts @@ -10,9 +10,8 @@ import type { AstAtRule, AstComment, AstDeclaration, - AstKeyFrameRule, - AstKeyframesAtRule, AstKeyframesRule, + AstKeyframesAtRule, AstNode, AstRule, AstRuleList, @@ -515,7 +514,7 @@ export function doParseSync( >; let item: TokenizeResult; - let node: AstAtRule | AstRule | AstKeyFrameRule | AstKeyframesAtRule | AstDeclaration | AstComment | null; + let node: AstAtRule | AstRule | AstKeyframesRule | AstKeyframesAtRule | AstDeclaration | AstComment | null; // @ts-ignore ignore error let parensMatch: number = 0; @@ -710,7 +709,7 @@ export function doParseSync( if (node != null) { if ("chi" in node) { - stack.push(node as AstAtRule | AstRule | AstKeyFrameRule); + stack.push(node as AstAtRule | AstRule | AstKeyframesRule); context = node as AstRuleList; } } else if (item.token.typ == EnumToken.BlockStartTokenType) { @@ -892,7 +891,12 @@ export function doParseSync( if (node != result.node) { replaceNodeOrValue( - result.parent as AstRule | AstAtRule | AstKeyframesAtRule | AstKeyFrameRule | AstStyleSheet, + result.parent as + | AstRule + | AstAtRule + | AstKeyframesAtRule + | AstKeyframesRule + | AstStyleSheet, result.node, node, ); @@ -1865,7 +1869,7 @@ export async function doParse( const imports: AstAtRule[] = []; let item: TokenizeResult; - let node: AstAtRule | AstRule | AstKeyFrameRule | AstKeyframesAtRule | AstDeclaration | AstComment | null; + let node: AstAtRule | AstRule | AstKeyframesRule | AstKeyframesAtRule | AstDeclaration | AstComment | null; // @ts-ignore ignore error let isAsync: boolean = typeof iter[Symbol.asyncIterator] === "function"; @@ -2064,7 +2068,7 @@ export async function doParse( if (node != null) { if ("chi" in node) { - stack.push(node as AstAtRule | AstRule | AstKeyFrameRule); + stack.push(node as AstAtRule | AstRule | AstKeyframesRule); context = node as AstRuleList; } else if (node.typ == EnumToken.AtRuleNodeType && (node as AstAtRule).nam === "import") { imports.push(node); @@ -2229,7 +2233,7 @@ export async function doParse( break; } - + if (options.visitor != null) { let parens: Token[] | null; for (const result of walk(ast)) { @@ -2336,7 +2340,12 @@ export async function doParse( if (node != result.node) { replaceNodeOrValue( - result.parent as AstRule | AstAtRule | AstKeyframesAtRule | AstKeyFrameRule | AstStyleSheet, + result.parent as + | AstRule + | AstAtRule + | AstKeyframesAtRule + | AstKeyframesRule + | AstStyleSheet, result.node, node, ); @@ -3405,7 +3414,7 @@ function parseNode( errors: ErrorDescription[], stats: ParseResultStats, invalidNodes: AstNode[], -): AstRule | AstAtRule | AstKeyFrameRule | AstKeyframesAtRule | AstDeclaration | AstComment | null { +): AstRule | AstAtRule | AstKeyframesRule | AstKeyframesAtRule | AstDeclaration | AstComment | null { let i: number = 0; if (tokens.at(-1)?.typ === EnumToken.EOFTokenType) { diff --git a/src/lib/parser/utils/declaration.ts b/src/lib/parser/utils/declaration.ts index cf133c44..e30b807d 100644 --- a/src/lib/parser/utils/declaration.ts +++ b/src/lib/parser/utils/declaration.ts @@ -1,7 +1,7 @@ import type { AstAtRule, AstDeclaration, - AstKeyFrameRule, + AstKeyframesRule, AstRule, AstStyleSheet, AtRuleToken, @@ -91,7 +91,7 @@ function parseGridTemplate(template: string): string { export function parseDeclaration( tokens: Token[], - parent: AstRule | AstAtRule | AstKeyFrameRule | AstStyleSheet | AtRuleToken | null, + parent: AstRule | AstAtRule | AstKeyframesRule | AstStyleSheet | AtRuleToken | null, options: ParserOptions, errors: ErrorDescription[], ): AstDeclaration | RawNodeToken { diff --git a/src/lib/parser/utils/selector.ts b/src/lib/parser/utils/selector.ts index 41ddb815..456a88c9 100644 --- a/src/lib/parser/utils/selector.ts +++ b/src/lib/parser/utils/selector.ts @@ -2,7 +2,7 @@ import type { Token, AstRule, AstAtRule, - AstKeyFrameRule, + AstKeyframesRule, AstKeyframesAtRule, AstStyleSheet, ParserOptions, @@ -36,10 +36,10 @@ import { trimWhiteSpace } from "../parse.ts"; export function parseSelector( tokens: Token[], - context: AtRuleToken | AstRule | AstAtRule | AstKeyFrameRule | AstKeyframesAtRule | AstStyleSheet | null, + context: AtRuleToken | AstRule | AstAtRule | AstKeyframesRule | AstKeyframesAtRule | AstStyleSheet | null, options: ParserOptions, errors: ErrorDescription[], -): AstRule | AstKeyFrameRule { +): AstRule | AstKeyframesRule { if (context?.typ === EnumToken.KeyframesAtRuleNodeType) { const result = matchAllSyntaxes( getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, "keyframe-selectors"), @@ -112,7 +112,7 @@ export function parseSelector( [TOKENS]: tokens.length === 0 ? null : tokens, [STATE]: result.success ? EnumAstNodeStatus.Validated : EnumAstNodeStatus.Invalid, [ERRORS]: result.errors, - } as AstKeyFrameRule; + } as AstKeyframesRule; } const stack: Token[] = []; diff --git a/src/lib/parser/utils/token.ts b/src/lib/parser/utils/token.ts index f792cff7..134340eb 100644 --- a/src/lib/parser/utils/token.ts +++ b/src/lib/parser/utils/token.ts @@ -1,9 +1,8 @@ import type { AstAtRule, AstDeclaration, - AstKeyFrameRule, - AstKeyframesAtRule, AstKeyframesRule, + AstKeyframesAtRule, AstNode, AstRule, } from "../../../@types/ast.d.ts"; @@ -81,7 +80,7 @@ export function replaceNodeOrValue( | ParensToken | AstAtRule | AstKeyframesAtRule - | AstKeyFrameRule + | AstKeyframesRule | AstRule | AstKeyframesRule ).chi as Token[]) ?? parent); diff --git a/src/lib/renderer/render.ts b/src/lib/renderer/render.ts index aed9f15a..0e2db5fb 100644 --- a/src/lib/renderer/render.ts +++ b/src/lib/renderer/render.ts @@ -421,7 +421,6 @@ function renderAstNode( // @ts-ignore let children: string = ""; let str: string = ""; - // let previousStr: string = ""; const indent: string = indents[level]; const indentSub: string = indents[level + 1]; @@ -436,7 +435,7 @@ function renderAstNode( case EnumToken.CommentNodeType: case EnumToken.CDOCOMMNodeType: if ((data).val.startsWith("/*# sourceMappingURL=")) { - // ignore sourcemap + // ignore sourcemap comment return ""; } @@ -510,15 +509,6 @@ function renderAstNode( ? "" : (node).val; } else if (node.typ == EnumToken.DeclarationNodeType) { - // if (!(node).nam.startsWith("--") && (node).val.length === 0) { - // // @ts-ignore - // errors.push({ - // action: "ignore", - // message: `render: invalid declaration ${JSON.stringify(node)}`, - // location: node[LOC], - // }); - // return ""; - // } str = `${(node).nam}:${options.indent}${(options.minify ? filterValues((node).val) @@ -598,27 +588,6 @@ function renderAstNode( return prelude + children + end; - // case EnumToken.CssVariableTokenType: - // case EnumToken.CssVariableImportTokenType: - // return `@value ${(data).val}:${options.indent}${filterValues( - // options.minify - // ? (data).val - // : (data).val, - // ) - // .reduce(reducer, "") - // .trim()};`; - - // case EnumToken.CssVariableDeclarationMapTokenType: - // return `@value ${filterValues((data as CssVariableMapTokenType).vars) - // .reduce((acc, curr) => acc + renderValue(curr), "") - // .trim()} from ${filterValues((data as CssVariableMapTokenType).from) - // .reduce((acc, curr) => acc + renderValue(curr), "") - // .trim()};`; - - // case EnumToken.InvalidDeclarationNodeType: - // case EnumToken.InvalidRuleNodeType: - // case EnumToken.InvalidAtRuleNodeType: - default: return ""; } diff --git a/src/lib/validation/match.ts b/src/lib/validation/match.ts index 0458949b..44164031 100644 --- a/src/lib/validation/match.ts +++ b/src/lib/validation/match.ts @@ -49,12 +49,20 @@ const config: ValidationConfiguration = getSyntaxConfig(); // @ts-expect-error const allValues = config.declarations.all!.syntax.split(/[\s|]+/g) as string[]; +/** + * @type {Array.} + */ export const funcTypes: EnumToken[] = [ ...tokensfuncDefMap.values(), EnumToken.FunctionTokenType, EnumToken.PseudoClassFuncTokenType, ]; +/** + * trim leading and trailing whitespace + * @param tokens + * @returns + */ export function trimArray(tokens: Token[]): Token[] { while (tokens[0]?.typ === EnumToken.WhitespaceTokenType) { tokens.shift(); @@ -67,6 +75,11 @@ export function trimArray(tokens: Token[]): Token[] { return tokens; } +/** + * is a media feature + * @param featureName + * @returns + */ export function isMFName(featureName: string): boolean { // @ts-expect-error return featureName.startsWith("--") || config.mediaFeatures[featureName.toLowerCase()] != null; @@ -191,6 +204,11 @@ export function isMFValue( }; } +/** + * create validation context + * @param tokens + * @returns + */ export function createValidationContext(tokens: Token[]): ValidationContext { tokens = trimArray(tokens.filter((t) => t.typ !== EnumToken.CommentTokenType)); @@ -392,6 +410,14 @@ export function createValidationContext(tokens: Token[]): ValidationContext { return token; } +/** + * match selector syntax + * @param stream + * @param errors + * @param options + * @param nested + * @returns + */ export function matchSelectorSyntax( stream: Token[], errors: ErrorDescription[], @@ -966,6 +992,13 @@ export function matchSelectorSyntax( return { success, errors }; } +/** + * matches all syntaxes + * @param syntaxes + * @param context + * @param options + * @returns + */ export function matchAllSyntaxes( syntaxes: ValidationToken[] | null, context: ValidationContext, @@ -1018,6 +1051,13 @@ export function matchAllSyntaxes( }; } +/** + * matches a list of syntaxes + * @param syntax + * @param context + * @param options + * @returns + */ function matchListSyntax( syntax: ValidationToken, context: ValidationContext, @@ -1079,6 +1119,13 @@ function matchListSyntax( }; } +/** + * matches a list of syntaxes + * @param syntax + * @param context + * @param options + * @returns + */ export function matchOccurenceSyntax( syntax: ValidationToken, context: ValidationContext, @@ -1134,6 +1181,13 @@ export function matchOccurenceSyntax( return result as ValidationMatch; } +/** + * matches a list of syntaxes + * @param syntaxes + * @param context + * @param options + * @returns + */ function matchSyntax( syntaxes: ValidationToken[] | null, context: ValidationContext, @@ -1967,6 +2021,13 @@ function matchSyntax( }; } +/** + * matches a column of syntaxes + * @param syntax + * @param context + * @param options + * @returns + */ function matchColumnSyntax( syntax: ValidationColumnToken, context: ValidationContext, @@ -2017,6 +2078,13 @@ function matchColumnSyntax( }; } +/** + * matches an ampersand of syntaxes + * @param syntax + * @param context + * @param options + * @returns + */ function matchAmpersandSyntax( syntax: ValidationAmpersandToken, context: ValidationContext, @@ -2046,6 +2114,13 @@ function matchAmpersandSyntax( return result!; } +/** + * matches a property + * @param property + * @param context + * @param options + * @returns + */ function matchProperty( property: ValidationPropertyToken, context: ValidationContext, @@ -2998,6 +3073,13 @@ function matchProperty( }; } +/** + * matches a repeatable syntax + * @param syntax + * @param context + * @param options + * @returns + */ function matchRepeatableSyntax( syntax: ValidationToken, context: ValidationContext, diff --git a/src/node.ts b/src/node.ts index 56ee1a39..85787661 100644 --- a/src/node.ts +++ b/src/node.ts @@ -1,5 +1,4 @@ import type { - AstComment, AstNode, LoadResult, ParseInfo, @@ -21,7 +20,7 @@ import { createReadStream } from "node:fs"; import { lstat, readFile } from "node:fs/promises"; import { doParse, doParseSync } from "./lib/parser/parse.ts"; import { doRender } from "./lib/renderer/render.ts"; -import { EnumToken, ModuleScopeEnumOptions } from "./lib/ast/types.ts"; +import { ModuleScopeEnumOptions } from "./lib/ast/types.ts"; import { tokenize, tokenizeStream } from "./lib/parser/tokenize.ts"; import { dirname, matchUrl, resolve } from "./lib/fs/resolve.ts"; import { ResponseType } from "./types.ts"; @@ -252,6 +251,7 @@ export function parseSync(options: ParseInputOptions & ParserSyncOptions): Parse /** * Parse css * @param args + * @private * * Parsing a string * @@ -362,11 +362,12 @@ export function transformSync(options: ParseInputOptions & TransformSyncOptions) * ``` * * @param args + * @private */ export function transformSync( ...args: [string, TransformSyncOptions?] | [ParseInputOptions & TransformSyncOptions] ): TransformResult { - let options: (ParseInputOptions & TransformSyncOptions) | TransformSyncOptions; + let options: (ParseInputStreamOptions & TransformSyncOptions) | TransformSyncOptions; let stream: string; if (typeof args[0] === "string") { @@ -425,12 +426,10 @@ export function transformSync( } /** - * Parse css + * Parse CSS * @param stream * @param options * - * @throws Error file not found - * * Example: * * ```ts @@ -442,7 +441,7 @@ export function transformSync( * console.log(result.ast); * ``` * - * parsing a Readable stream + * parsing a ReadableStream * * ```ts * @@ -457,7 +456,7 @@ export function transformSync( * console.log(result.ast); * ``` * - * Example using fetch and readable stream + * Parsing a file as a ReadableStream * * ```ts * @@ -554,6 +553,7 @@ export async function parse(options: ParseInputStreamOptions & ParserOptions): P * @param args * * @throws Error file not found + * @private * * Parsing a string * @@ -660,7 +660,7 @@ export async function parse( } /** - * Transform css file + * Transform CSS file * @param file url or path * @param options * @param asStream load file as stream @@ -695,7 +695,7 @@ export const transformFile = deprecate( ) as (file: string, options?: TransformOptions, asStream?: boolean) => Promise; /** - * Transform css + * Transform CSS * @param css * @param options * @@ -772,7 +772,7 @@ export async function transform( * console.log(result.code); * ``` * - * Example using fetch + * Parse a file as a ReadableStream * * ```ts * @@ -788,43 +788,16 @@ export async function transform( export async function transform(options: ParseInputStreamOptions & TransformOptions): Promise; /** - * Transform css + * Transform CSS * @param options * - * Parsing a string - * - * ```ts - * - * import {transform} from '@tbela99/css-parser'; - * - * // css string - * const result = await transform({input: css}); - * console.log(result.code); - * ``` - * - * Parsing a Readable stream + * Parsing a file * * ```ts * * import {transform} from '@tbela99/css-parser'; - * import {Readable} from "node:stream"; - * - * // usage: node index.ts < styles.css or cat styles.css | node index.ts - * - * const readableStream = Readable.toWeb(process.stdin); - * const result = await transform( {input: readableStream, beautify: true}); - * - * console.log(result.code); - * ``` - * - * Example using fetch - * - * ```ts - * - * import {transform} from '@tbela99/css-parser'; - * - * result = await transform({file: 'https://docs.deno.com/styles.css', beautify: true}); * + * const result = await transform( {file: 'https://docs.deno.com/styles.css', beautify: true}); * console.log(result.code); * ``` */ @@ -872,6 +845,7 @@ export async function transform(options: ParseInputFileOptions & TransformOption * console.log(result.code); * ``` * @param args + * @private */ export async function transform( ...args: diff --git a/src/web.ts b/src/web.ts index 11781e44..9d1b92e7 100644 --- a/src/web.ts +++ b/src/web.ts @@ -73,7 +73,7 @@ export { dirname, resolve, ResponseType }; * @throws Error file not found * * ```ts - * import {load, ResponseType} from '@tbela99/css-parser'; + * import {load, ResponseType} from '@tbela99/css-parser/web'; * const result = await load(file, '.', ResponseType.ArrayBuffer) as ArrayBuffer; * ``` */ @@ -123,7 +123,7 @@ export async function load( * * ```ts * - * import {render, ColorType} from '@tbela99/css-parser'; + * import {render, ColorType} from '@tbela99/css-parser/web'; * * const css = 'body { color: color(from hsl(0 100% 50%) xyz x y z); }'; * const parseResult = await parse(css); @@ -208,13 +208,26 @@ export async function parseFile( * * ```ts * - * import {parseSync} from '@tbela99/css-parser'; + * import {parseSync} from '@tbela99/css-parser/web'; * * // css string * let result = await parseSync(css, {nestingRules: true}); * console.log(result.ast); * ``` * + * parsing a ReadableStream + * + * ```ts + * + * import {parseSync} from '@tbela99/css-parser/web'; + * + * const response = await fetch('https://docs.deno.com/styles.css'); + * + * // css string + * const result = parseSync(response.body, {beautify: true}); + * console.log(result.code); + * ``` + * */ export function parseSync(stream: string, options?: ParserSyncOptions): ParseResult; @@ -227,13 +240,26 @@ export function parseSync(stream: string, options?: ParserSyncOptions): ParseRes * * ```ts * - * import {parseSync} from '@tbela99/css-parser'; + * import {parseSync} from '@tbela99/css-parser/web'; * * // css string - * let result = await parseSync({input: css, nestingRules: true}); + * let result = parseSync({input: css, nestingRules: true}); * console.log(result.ast); * ``` * + * parsing a ReadableStream + * + * ```ts + * + * import {parseSync} from '@tbela99/css-parser/web'; + * + * const response = await fetch('https://docs.deno.com/styles.css'); + * + * // css string + * const result = parseSync({input: response.body, beautify: true}); + * console.log(result.code); + * ``` + * */ export function parseSync(options: ParseInputOptions & ParserSyncOptions): ParseResult; @@ -241,12 +267,13 @@ export function parseSync(options: ParseInputOptions & ParserSyncOptions): Parse /** * Parse css * @param args + * @private * * Parsing a string * * ```ts * - * import {parseSync} from '@tbela99/css-parser'; + * import {parseSync} from '@tbela99/css-parser/web'; * * // css string * let result = await parseSync(css, {nestingRules: true}); @@ -311,11 +338,12 @@ export function parseSync( * Transform css * @param css * @param options + * @private * * * ```ts * - * import {transformSync} from '@tbela99/css-parser'; + * import {transformSync} from '@tbela99/css-parser/web'; * * // css string * const result = transformSync(css, {beautify: true}); @@ -329,9 +357,11 @@ export function transformSync(css: string, options?: TransformSyncOptions): Tran * Transform css * @param options * + * parsing a string + * * ```ts * - * import {transformSync} from '@tbela99/css-parser'; + * import {transformSync} from '@tbela99/css-parser/web'; * * // css string * const result = transformSync({input: css, beautify: true}); @@ -342,11 +372,11 @@ export function transformSync(css: string, options?: TransformSyncOptions): Tran export function transformSync(options: ParseInputOptions & TransformSyncOptions): TransformResult; /** - * Transform css + * Transform CSS * * ```ts * - * import {transformSync} from '@tbela99/css-parser'; + * import {transformSync} from '@tbela99/css-parser/web'; * * // css string * const result = transformSync(css); @@ -354,12 +384,13 @@ export function transformSync(options: ParseInputOptions & TransformSyncOptions) * ``` * * @param args + * @private */ export function transformSync( ...args: [string, TransformSyncOptions?] | [ParseInputOptions & TransformSyncOptions] ): TransformResult { let options: (ParseInputOptions & TransformSyncOptions) | TransformSyncOptions; - let stream: string; + let stream: string | ReadableStream; if (typeof args[0] === "string") { stream = args[0]; @@ -416,8 +447,98 @@ export function transformSync( } as TransformResult; } +/** + * Parse CSS + * @param stream + * @param options + * + * Example: + * + * ```ts + * + * import {parse} from '@tbela99/css-parser/web'; + * + * // css string + * let result = await parse(css); + * console.log(result.ast); + * ``` + * + * Parse a file as a ReadableStream + * + * ```ts + * + * import {parse} from '@tbela99/css-parser/web'; + * + * const response = await fetch('https://docs.deno.com/styles.css'); + * const result = await parse(response.body, {beautify: true}); + * + * console.log(result.ast); + * ``` + */ + export async function parse(stream: string | ReadableStream, options?: ParserOptions): Promise; + +/** + * Parse css + * @param options + * + * @throws Error file not found + * + * Parsing a file + * + * ```ts + * + * import {parse} from '@tbela99/css-parser/web'; + * + * const file = 'https://docs.deno.com/styles.css'; + * // css file or url + * let result = await parse({file}); + * console.log(result.ast); + * ``` + * + * Parsing a file as stream + * + * ```ts + * + * import {parse} from '@tbela99/css-parser/web'; + * + * const file = 'https://docs.deno.com/styles.css'; + * let result = await parse({file, asStream: true, beautify: true}); + * + * console.log(result.ast); + * ``` + * + */ export async function parse(options: ParseInputFileOptions & ParserOptions): Promise; + +/** + * Parse css + * @param options + * + * Parsing a string + * + * ```ts + * + * import {parse} from '@tbela99/css-parser/web'; + * + * // css string + * let result = await parse({input:css}); + * console.log(result.ast); + * ``` + * + * Parsing a Readable stream + * Parsing a file as a ReadableStream + * + * ```ts + * + * import {parse} from '@tbela99/css-parser/web'; + * + * const response = await fetch('https://docs.deno.com/styles.css'); + * const result = await parse({input: response.body, beautify: true}); + * + * console.log(result.ast); + * ``` + */ export async function parse(options: ParseInputStreamOptions & ParserOptions): Promise; /** @@ -446,6 +567,7 @@ export async function parse(options: ParseInputStreamOptions & ParserOptions): P * console.log(result.ast); * ``` * @param args + * @private */ export async function parse( ...args: @@ -517,7 +639,7 @@ export async function parse( } /** - * Transform css file + * Transform CSS file * @param file url or path * @param options * @param asStream load file as stream @@ -554,14 +676,85 @@ export async function transformFile( }); } +/** + * Transform CSS + * @param css + * @param options + * + * Parsing a string + * + * ```ts + * + * import {transform} from '@tbela99/css-parser/web'; + * + * // css string + * const result = await transform(css); + * console.log(result.code); + * ``` + * + * Parsing a Readable stream + * + * ```ts + * + * import {transform} from '@tbela99/css-parser/web'; + * + * const response = await fetch('https://docs.deno.com/styles.css'); + * result = await transform(response.body, {beautify: true}); + * + * console.log(result.code); + * ``` + */ export async function transform( css: string | ReadableStream, options: TransformOptions, ): Promise; -export async function transform(options: ParseInputFileOptions & TransformOptions): Promise; +/** + * Transform CSS + * @param options + * + * Parsing a string + * + * ```ts + * + * import {transform} from '@tbela99/css-parser/web'; + * + * // css string + * const result = await transform({input: css}); // or transform(css) + * console.log(result.code); + * ``` + * + * Parsing a file as a Readable stream + * + * ```ts + * + * import {transform} from '@tbela99/css-parser/web'; + * + * const response = await fetch('https://docs.deno.com/styles.css'); + * const result = await transform( {input: response.body, beautify: true}); + * + * console.log(result.code); + * ``` + * + */ export async function transform(options: ParseInputStreamOptions & TransformOptions): Promise; +/** + * Transform CSS + * @param options + * + * Parsing a file + * + * ```ts + * + * import {transform} from '@tbela99/css-parser/web'; + * + * const result = await transform( {file: 'https://docs.deno.com/styles.css', beautify: true}); + * console.log(result.code); + * ``` + */ +export async function transform(options: ParseInputFileOptions & TransformOptions): Promise; + /** * Transform css * @@ -582,6 +775,7 @@ export async function transform(options: ParseInputStreamOptions & TransformOpti * console.log(result.code); * ``` * @param args + * @private */ export async function transform( ...args: From ee7b8b9d9b6db3b9163c3355e3b609a969a53b86 Mon Sep 17 00:00:00 2001 From: Thierry Bela Nanga Date: Sun, 16 Aug 2026 22:01:54 -0400 Subject: [PATCH 06/22] bump version #146 --- .gitignore | 1 + benchmark/package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 1631267c..091c51e3 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ /ROADMAP.draft.md /.idea /.DS_Store +/benchmark /ROADMAP.md /package-lock.json test/*.ts diff --git a/benchmark/package.json b/benchmark/package.json index 84d10d42..a7e23016 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -10,7 +10,7 @@ "all": "npm run sizes && npm run bench && npm run report" }, "dependencies": { - "@tbela99/css-parser": "^1.4.9", + "@tbela99/css-parser": "^1.5.0", "@tbela99/css-parser2": "github:tbela99/css-parser#2279484", "clean-css": "^5.3.3", "css-tree": "^3.2.1", From 65f3c22e7bae22f3825e60e9142c8beffe741524 Mon Sep 17 00:00:00 2001 From: Thierry Bela Nanga Date: Sun, 16 Aug 2026 22:22:51 -0400 Subject: [PATCH 07/22] delete incorrect import #146 --- dist/index-umd-web.js | 120 +++++-- dist/index.cjs | 120 +++++-- dist/index.d.ts | 609 ++++++++++++++++++++++++++--------- dist/lib/ast/find.js | 9 +- dist/lib/ast/minify.js | 34 +- dist/lib/validation/match.js | 77 +++++ src/@types/ast.d.ts | 1 - src/@types/index.d.ts | 1 - 8 files changed, 752 insertions(+), 219 deletions(-) diff --git a/dist/index-umd-web.js b/dist/index-umd-web.js index 5171a65e..768c4117 100644 --- a/dist/index-umd-web.js +++ b/dist/index-umd-web.js @@ -11705,11 +11705,19 @@ const config$3 = getSyntaxConfig(); // @ts-expect-error const allValues = config$3.declarations.all.syntax.split(/[\s|]+/g); + /** + * @type {Array.} + */ const funcTypes = [ ...tokensfuncDefMap.values(), exports.EnumToken.FunctionTokenType, exports.EnumToken.PseudoClassFuncTokenType, ]; + /** + * trim leading and trailing whitespace + * @param tokens + * @returns + */ function trimArray(tokens) { while (tokens[0]?.typ === exports.EnumToken.WhitespaceTokenType) { tokens.shift(); @@ -11810,6 +11818,11 @@ success: true, }; } + /** + * create validation context + * @param tokens + * @returns + */ function createValidationContext(tokens) { tokens = trimArray(tokens.filter((t) => t.typ !== exports.EnumToken.CommentTokenType)); if (tokens.at(-1)?.typ === exports.EnumToken.ImportantTokenType) { @@ -11973,6 +11986,14 @@ }; return token; } + /** + * match selector syntax + * @param stream + * @param errors + * @param options + * @param nested + * @returns + */ function matchSelectorSyntax(stream, errors, options, nested = true) { const stack = []; const tokens = []; @@ -12411,6 +12432,13 @@ stream.push(...tokens); return { success, errors }; } + /** + * matches all syntaxes + * @param syntaxes + * @param context + * @param options + * @returns + */ function matchAllSyntaxes(syntaxes, context, options) { const result = matchSyntax(syntaxes, context, { ...options, @@ -12449,6 +12477,13 @@ syntaxToken: !result.success ? result.syntaxToken : null, }; } + /** + * matches a list of syntaxes + * @param syntax + * @param context + * @param options + * @returns + */ function matchListSyntax(syntax, context, options) { const { isList, match, isOptional, ...rest } = syntax; let success = true; @@ -12493,6 +12528,13 @@ token: context.peek(), }; } + /** + * matches a list of syntaxes + * @param syntax + * @param context + * @param options + * @returns + */ function matchOccurenceSyntax(syntax, context, options) { const { match, ...rest } = syntax; let result = null; @@ -12534,6 +12576,13 @@ } return result; } + /** + * matches a list of syntaxes + * @param syntaxes + * @param context + * @param options + * @returns + */ function matchSyntax(syntaxes, context, options) { if (syntaxes == null) { return { @@ -13174,6 +13223,13 @@ errors: [], }; } + /** + * matches a column of syntaxes + * @param syntax + * @param context + * @param options + * @returns + */ function matchColumnSyntax(syntax, context, options) { let syntaxes = syntax.chi.slice(); let i = 0; @@ -13210,6 +13266,13 @@ errors: [], }; } + /** + * matches an ampersand of syntaxes + * @param syntax + * @param context + * @param options + * @returns + */ function matchAmpersandSyntax(syntax, context, options) { const syntaxes = [syntax.l, syntax.r]; let result; @@ -13228,6 +13291,13 @@ } return result; } + /** + * matches a property + * @param property + * @param context + * @param options + * @returns + */ function matchProperty(property, context, options) { let success = false; let t = context.peek()?.typ; @@ -13960,6 +14030,13 @@ errors: [], }; } + /** + * matches a repeatable syntax + * @param syntax + * @param context + * @param options + * @returns + */ function matchRepeatableSyntax(syntax, context, options) { const { isRepeatable, isOptional, isMandatatoryGroup, isRepeatableAtLeastOnce, ...rest } = syntax; let result = null; @@ -21089,6 +21166,11 @@ return null; } /** + * + * @param ast + * @param matcher + * @returns + * * Search the ast sub-tree by checking each node's value token and return the first match * ```ts @@ -21114,10 +21196,6 @@ console.log({node, value}); ``` - * - * @param ast - * @param matcher - * @returns */ function findByValue(ast, matcher) { let source; @@ -22742,29 +22820,29 @@ * @param context * @private */ - function minify(ast, opt = {}, recursive = false, errors, nestingContent, context = {}) { + function minify(ast, options = {}, recursive = false, errors, nestingContent, context = {}) { let preprocess = false; let postprocess = false; let parents; let replacement; // @ts-ignore - let { sourcemap, module, ...options } = opt; - if (!("features" in options)) { + let { sourcemap, module, ...options2 } = options; + if (!("features" in options2)) { // @ts-ignore - options = { + options2 = { removeDuplicateDeclarations: true, computeShorthand: true, computeCalcExpression: true, removePrefix: false, features: [], - ...options, + ...options2, }; for (const feature of features) { - feature.register(options); + feature.register(options2); } - options.features.sort((a, b) => a.ordering - b.ordering); + options2.features.sort((a, b) => a.ordering - b.ordering); } - for (const feature of options.features) { + for (const feature of options2.features) { if (feature.processMode & exports.FeatureWalkMode.Pre) { preprocess = true; } @@ -22779,7 +22857,7 @@ continue; } replacement = parent; - for (const feature of options.features) { + for (const feature of options2.features) { if ((feature.processMode & exports.FeatureWalkMode.Pre) === 0 || (feature.accept != null && !feature.accept.has(parent.typ))) { continue; @@ -22789,7 +22867,7 @@ ? replacement.sel : replacement.nam); } - const result = feature.run(replacement, options, parent[PARENT] ?? ast, context, exports.FeatureWalkMode.Pre); + const result = feature.run(replacement, options2, parent[PARENT] ?? ast, context, exports.FeatureWalkMode.Pre); if (result != null) { replacement = result; } @@ -22808,14 +22886,14 @@ } } } - for (const feature of options.features) { + for (const feature of options2.features) { if (feature.processMode & exports.FeatureWalkMode.Pre && "cleanup" in feature) { // @ts-ignore - feature.cleanup(ast, options, context, exports.FeatureWalkMode.Pre); + feature.cleanup(ast, options2, context, exports.FeatureWalkMode.Pre); } } } - doMinify(ast, options, recursive, errors, nestingContent, context); + doMinify(ast, options2, recursive, errors, nestingContent, context); parents = new Set([ast]); for (const parent of parents) { if (parent.typ == exports.EnumToken.CommentTokenType || parent.typ == exports.EnumToken.CDOCOMMTokenType) { @@ -22823,12 +22901,12 @@ } replacement = parent; if (postprocess) { - for (const feature of options.features) { + for (const feature of options2.features) { if ((feature.processMode & exports.FeatureWalkMode.Post) === 0 || (feature.accept != null && !feature.accept.has(parent.typ))) { continue; } - const result = feature.run(replacement, options, parent[PARENT] ?? ast, context, exports.FeatureWalkMode.Post); + const result = feature.run(replacement, options2, parent[PARENT] ?? ast, context, exports.FeatureWalkMode.Post); if (result != null) { replacement = result; } @@ -22849,10 +22927,10 @@ } } if (postprocess) { - for (const feature of options.features) { + for (const feature of options2.features) { if (feature.processMode & exports.FeatureWalkMode.Post && "cleanup" in feature) { // @ts-ignore - feature.cleanup(ast, options, context, exports.FeatureWalkMode.Post); + feature.cleanup(ast, options2, context, exports.FeatureWalkMode.Post); } } } diff --git a/dist/index.cjs b/dist/index.cjs index c60b7ab5..481a526d 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -11708,11 +11708,19 @@ function cloneNode(node, cloneChildren = false, cloneMap = null) { const config$3 = getSyntaxConfig(); // @ts-expect-error const allValues = config$3.declarations.all.syntax.split(/[\s|]+/g); +/** + * @type {Array.} + */ const funcTypes = [ ...tokensfuncDefMap.values(), exports.EnumToken.FunctionTokenType, exports.EnumToken.PseudoClassFuncTokenType, ]; +/** + * trim leading and trailing whitespace + * @param tokens + * @returns + */ function trimArray(tokens) { while (tokens[0]?.typ === exports.EnumToken.WhitespaceTokenType) { tokens.shift(); @@ -11813,6 +11821,11 @@ function isMFValue(featureName, tokens, isMFRange) { success: true, }; } +/** + * create validation context + * @param tokens + * @returns + */ function createValidationContext(tokens) { tokens = trimArray(tokens.filter((t) => t.typ !== exports.EnumToken.CommentTokenType)); if (tokens.at(-1)?.typ === exports.EnumToken.ImportantTokenType) { @@ -11976,6 +11989,14 @@ function createValidationContext(tokens) { }; return token; } +/** + * match selector syntax + * @param stream + * @param errors + * @param options + * @param nested + * @returns + */ function matchSelectorSyntax(stream, errors, options, nested = true) { const stack = []; const tokens = []; @@ -12414,6 +12435,13 @@ function matchSelectorSyntax(stream, errors, options, nested = true) { stream.push(...tokens); return { success, errors }; } +/** + * matches all syntaxes + * @param syntaxes + * @param context + * @param options + * @returns + */ function matchAllSyntaxes(syntaxes, context, options) { const result = matchSyntax(syntaxes, context, { ...options, @@ -12452,6 +12480,13 @@ function matchAllSyntaxes(syntaxes, context, options) { syntaxToken: !result.success ? result.syntaxToken : null, }; } +/** + * matches a list of syntaxes + * @param syntax + * @param context + * @param options + * @returns + */ function matchListSyntax(syntax, context, options) { const { isList, match, isOptional, ...rest } = syntax; let success = true; @@ -12496,6 +12531,13 @@ function matchListSyntax(syntax, context, options) { token: context.peek(), }; } +/** + * matches a list of syntaxes + * @param syntax + * @param context + * @param options + * @returns + */ function matchOccurenceSyntax(syntax, context, options) { const { match, ...rest } = syntax; let result = null; @@ -12537,6 +12579,13 @@ function matchOccurenceSyntax(syntax, context, options) { } return result; } +/** + * matches a list of syntaxes + * @param syntaxes + * @param context + * @param options + * @returns + */ function matchSyntax(syntaxes, context, options) { if (syntaxes == null) { return { @@ -13177,6 +13226,13 @@ function matchSyntax(syntaxes, context, options) { errors: [], }; } +/** + * matches a column of syntaxes + * @param syntax + * @param context + * @param options + * @returns + */ function matchColumnSyntax(syntax, context, options) { let syntaxes = syntax.chi.slice(); let i = 0; @@ -13213,6 +13269,13 @@ function matchColumnSyntax(syntax, context, options) { errors: [], }; } +/** + * matches an ampersand of syntaxes + * @param syntax + * @param context + * @param options + * @returns + */ function matchAmpersandSyntax(syntax, context, options) { const syntaxes = [syntax.l, syntax.r]; let result; @@ -13231,6 +13294,13 @@ function matchAmpersandSyntax(syntax, context, options) { } return result; } +/** + * matches a property + * @param property + * @param context + * @param options + * @returns + */ function matchProperty(property, context, options) { let success = false; let t = context.peek()?.typ; @@ -13963,6 +14033,13 @@ function matchProperty(property, context, options) { errors: [], }; } +/** + * matches a repeatable syntax + * @param syntax + * @param context + * @param options + * @returns + */ function matchRepeatableSyntax(syntax, context, options) { const { isRepeatable, isOptional, isMandatatoryGroup, isRepeatableAtLeastOnce, ...rest } = syntax; let result = null; @@ -21092,6 +21169,11 @@ function find(ast, matcher) { return null; } /** + * + * @param ast + * @param matcher + * @returns + * * Search the ast sub-tree by checking each node's value token and return the first match * ```ts @@ -21117,10 +21199,6 @@ button { console.log({node, value}); ``` - * - * @param ast - * @param matcher - * @returns */ function findByValue(ast, matcher) { let source; @@ -22745,29 +22823,29 @@ const features = Object.values(allFeatures).sort((a, b) => a.ordering - b.orderi * @param context * @private */ -function minify(ast, opt = {}, recursive = false, errors, nestingContent, context = {}) { +function minify(ast, options = {}, recursive = false, errors, nestingContent, context = {}) { let preprocess = false; let postprocess = false; let parents; let replacement; // @ts-ignore - let { sourcemap, module, ...options } = opt; - if (!("features" in options)) { + let { sourcemap, module, ...options2 } = options; + if (!("features" in options2)) { // @ts-ignore - options = { + options2 = { removeDuplicateDeclarations: true, computeShorthand: true, computeCalcExpression: true, removePrefix: false, features: [], - ...options, + ...options2, }; for (const feature of features) { - feature.register(options); + feature.register(options2); } - options.features.sort((a, b) => a.ordering - b.ordering); + options2.features.sort((a, b) => a.ordering - b.ordering); } - for (const feature of options.features) { + for (const feature of options2.features) { if (feature.processMode & exports.FeatureWalkMode.Pre) { preprocess = true; } @@ -22782,7 +22860,7 @@ function minify(ast, opt = {}, recursive = false, errors, nestingContent, contex continue; } replacement = parent; - for (const feature of options.features) { + for (const feature of options2.features) { if ((feature.processMode & exports.FeatureWalkMode.Pre) === 0 || (feature.accept != null && !feature.accept.has(parent.typ))) { continue; @@ -22792,7 +22870,7 @@ function minify(ast, opt = {}, recursive = false, errors, nestingContent, contex ? replacement.sel : replacement.nam); } - const result = feature.run(replacement, options, parent[PARENT] ?? ast, context, exports.FeatureWalkMode.Pre); + const result = feature.run(replacement, options2, parent[PARENT] ?? ast, context, exports.FeatureWalkMode.Pre); if (result != null) { replacement = result; } @@ -22811,14 +22889,14 @@ function minify(ast, opt = {}, recursive = false, errors, nestingContent, contex } } } - for (const feature of options.features) { + for (const feature of options2.features) { if (feature.processMode & exports.FeatureWalkMode.Pre && "cleanup" in feature) { // @ts-ignore - feature.cleanup(ast, options, context, exports.FeatureWalkMode.Pre); + feature.cleanup(ast, options2, context, exports.FeatureWalkMode.Pre); } } } - doMinify(ast, options, recursive, errors, nestingContent, context); + doMinify(ast, options2, recursive, errors, nestingContent, context); parents = new Set([ast]); for (const parent of parents) { if (parent.typ == exports.EnumToken.CommentTokenType || parent.typ == exports.EnumToken.CDOCOMMTokenType) { @@ -22826,12 +22904,12 @@ function minify(ast, opt = {}, recursive = false, errors, nestingContent, contex } replacement = parent; if (postprocess) { - for (const feature of options.features) { + for (const feature of options2.features) { if ((feature.processMode & exports.FeatureWalkMode.Post) === 0 || (feature.accept != null && !feature.accept.has(parent.typ))) { continue; } - const result = feature.run(replacement, options, parent[PARENT] ?? ast, context, exports.FeatureWalkMode.Post); + const result = feature.run(replacement, options2, parent[PARENT] ?? ast, context, exports.FeatureWalkMode.Post); if (result != null) { replacement = result; } @@ -22852,10 +22930,10 @@ function minify(ast, opt = {}, recursive = false, errors, nestingContent, contex } } if (postprocess) { - for (const feature of options.features) { + for (const feature of options2.features) { if (feature.processMode & exports.FeatureWalkMode.Post && "cleanup" in feature) { // @ts-ignore - feature.cleanup(ast, options, context, exports.FeatureWalkMode.Post); + feature.cleanup(ast, options2, context, exports.FeatureWalkMode.Post); } } } diff --git a/dist/index.d.ts b/dist/index.d.ts index c1160d41..ae5c71fd 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -980,7 +980,7 @@ declare const OPTIMIZED: unique symbol; */ export declare interface LiteralToken extends BaseToken { /** - * literal type + * @inheritdoc */ typ: EnumToken.LiteralTokenType; /** @@ -994,7 +994,7 @@ export declare interface LiteralToken extends BaseToken { */ export declare interface ClassSelectorToken extends BaseToken { /** - * class selector type + * @inheritdoc */ typ: EnumToken.ClassSelectorTokenType; /** @@ -1008,7 +1008,7 @@ export declare interface ClassSelectorToken extends BaseToken { */ export declare interface InvalidClassSelectorToken extends BaseToken { /** - * invalid class selector type + * @inheritdoc */ typ: EnumToken.InvalidClassSelectorTokenType; /** @@ -1022,7 +1022,7 @@ export declare interface InvalidClassSelectorToken extends BaseToken { */ export declare interface UniversalSelectorToken extends BaseToken { /** - * universal selector type + * @inheritdoc */ typ: EnumToken.UniversalSelectorTokenType; } @@ -1032,7 +1032,7 @@ export declare interface UniversalSelectorToken extends BaseToken { */ export declare interface IdentToken extends BaseToken { /** - * ident type + * @inheritdoc */ typ: EnumToken.IdenTokenType; /** @@ -1046,7 +1046,7 @@ export declare interface IdentToken extends BaseToken { */ export declare interface IdentListToken extends BaseToken { /** - * ident list type + * @inheritdoc */ typ: EnumToken.IdenListTokenType; /** @@ -1060,7 +1060,7 @@ export declare interface IdentListToken extends BaseToken { */ export declare interface DashedIdentToken extends BaseToken { /** - * ident type + * @inheritdoc */ typ: EnumToken.DashedIdenTokenType; /** @@ -1074,7 +1074,7 @@ export declare interface DashedIdentToken extends BaseToken { */ export declare interface CommaToken extends BaseToken { /** - * comma type + * @inheritdoc */ typ: EnumToken.CommaTokenType; } @@ -1084,7 +1084,7 @@ export declare interface CommaToken extends BaseToken { */ export declare interface ColonToken extends BaseToken { /** - * colon type ':' + * @inheritdoc */ typ: EnumToken.ColonTokenType; } @@ -1094,7 +1094,7 @@ export declare interface ColonToken extends BaseToken { */ export declare interface DoubleColonToken extends BaseToken { /** - * double colon type '::' + * @inheritdoc */ typ: EnumToken.DoubleColonTokenType; } @@ -1104,7 +1104,7 @@ export declare interface DoubleColonToken extends BaseToken { */ export declare interface SemiColonToken extends BaseToken { /** - * semicolon type + * @inheritdoc */ typ: EnumToken.SemiColonTokenType; } @@ -1114,7 +1114,7 @@ export declare interface SemiColonToken extends BaseToken { */ export declare interface NestingSelectorToken extends BaseToken { /** - * nesting selector type + * @inheritdoc */ typ: EnumToken.NestingSelectorTokenType; } @@ -1124,7 +1124,7 @@ export declare interface NestingSelectorToken extends BaseToken { */ export declare interface NumberToken extends BaseToken { /** - * number type + * @inheritdoc */ typ: EnumToken.NumberTokenType; /** @@ -1142,7 +1142,7 @@ export declare interface NumberToken extends BaseToken { */ export declare interface AtRuleToken extends BaseToken { /** - * at rule type + * @inheritdoc */ typ: EnumToken.AtRuleTokenType; /** @@ -1160,7 +1160,7 @@ export declare interface AtRuleToken extends BaseToken { */ export declare interface PercentageToken extends BaseToken { /** - * percentage type + * @inheritdoc */ typ: EnumToken.PercentageTokenType; /** @@ -1174,7 +1174,7 @@ export declare interface PercentageToken extends BaseToken { */ export declare interface FlexToken extends BaseToken { /** - * flex type + * @inheritdoc */ typ: EnumToken.FlexTokenType; /** @@ -1216,7 +1216,7 @@ export declare interface FunctionToken extends BaseToken { */ export declare interface GridTemplateFuncToken extends BaseToken { /** - * function type + * @inheritdoc */ typ: EnumToken.GridTemplateFuncTokenType; /** @@ -1234,7 +1234,7 @@ export declare interface GridTemplateFuncToken extends BaseToken { */ export declare interface FunctionURLToken extends BaseToken { /** - * function type + * @inheritdoc */ typ: EnumToken.UrlFunctionTokenType; /** @@ -1252,7 +1252,7 @@ export declare interface FunctionURLToken extends BaseToken { */ export declare interface FunctionImageToken extends BaseToken { /** - * function type + * @inheritdoc */ typ: EnumToken.ImageFunctionTokenType; /** @@ -1279,7 +1279,7 @@ export declare interface FunctionImageToken extends BaseToken { */ export declare interface TimingFunctionToken extends BaseToken { /** - * timing function type + * @inheritdoc */ typ: EnumToken.TimingFunctionTokenType; /** @@ -1297,7 +1297,7 @@ export declare interface TimingFunctionToken extends BaseToken { */ export declare interface TimelineFunctionToken extends BaseToken { /** - * timeline function type + * @inheritdoc */ typ: EnumToken.TimelineFunctionTokenType; /** @@ -1315,7 +1315,7 @@ export declare interface TimelineFunctionToken extends BaseToken { */ export declare interface StringToken extends BaseToken { /** - * string type + * @inheritdoc */ typ: EnumToken.StringTokenType; /** @@ -1329,7 +1329,7 @@ export declare interface StringToken extends BaseToken { */ export declare interface BadStringToken extends BaseToken { /** - * bad string type + * @inheritdoc */ typ: EnumToken.BadStringTokenType; /** @@ -1343,7 +1343,7 @@ export declare interface BadStringToken extends BaseToken { */ export declare interface UnclosedStringToken extends BaseToken { /** - * unclosed string type + * @inheritdoc */ typ: EnumToken.UnclosedStringTokenType; /** @@ -1357,7 +1357,7 @@ export declare interface UnclosedStringToken extends BaseToken { */ export declare interface DimensionToken extends BaseToken { /** - * dimension type + * @inheritdoc */ typ: EnumToken.DimensionTokenType; /** @@ -1375,7 +1375,7 @@ export declare interface DimensionToken extends BaseToken { */ export declare interface LengthToken extends BaseToken { /** - * length type + * @inheritdoc */ typ: EnumToken.LengthTokenType; /** @@ -1393,7 +1393,7 @@ export declare interface LengthToken extends BaseToken { */ export declare interface AngleToken extends BaseToken { /** - * angle type + * @inheritdoc */ typ: EnumToken.AngleTokenType; /** @@ -1411,7 +1411,7 @@ export declare interface AngleToken extends BaseToken { */ export declare interface TimeToken extends BaseToken { /** - * time type + * @inheritdoc */ typ: EnumToken.TimeTokenType; /** @@ -1419,7 +1419,7 @@ export declare interface TimeToken extends BaseToken { */ val: number | FractionToken; /** - * time unit + * time unit */ unit: "ms" | "s"; } @@ -1429,7 +1429,7 @@ export declare interface TimeToken extends BaseToken { */ export declare interface FrequencyToken extends BaseToken { /** - * frequency type + * @inheritdoc */ typ: EnumToken.FrequencyTokenType; /** @@ -1447,7 +1447,7 @@ export declare interface FrequencyToken extends BaseToken { */ export declare interface ResolutionToken extends BaseToken { /** - * resolution type + * @inheritdoc */ typ: EnumToken.ResolutionTokenType; /** @@ -1465,7 +1465,7 @@ export declare interface ResolutionToken extends BaseToken { */ export declare interface HashToken extends BaseToken { /** - * hash type + * @inheritdoc */ typ: EnumToken.HashTokenType; /** @@ -1479,7 +1479,7 @@ export declare interface HashToken extends BaseToken { */ export declare interface BlockStartToken extends BaseToken { /** - * block start type + * @inheritdoc */ typ: EnumToken.BlockStartTokenType; } @@ -1489,7 +1489,7 @@ export declare interface BlockStartToken extends BaseToken { */ export declare interface BlockEndToken extends BaseToken { /** - * block end type + * @inheritdoc */ typ: EnumToken.BlockEndTokenType; } @@ -1499,7 +1499,7 @@ export declare interface BlockEndToken extends BaseToken { */ export declare interface AttrStartToken extends BaseToken { /** - * attribute start type + * @inheritdoc */ typ: EnumToken.AttrStartTokenType; /** @@ -1513,7 +1513,7 @@ export declare interface AttrStartToken extends BaseToken { */ export declare interface AttrEndToken extends BaseToken { /** - * attribute end type + * @inheritdoc */ typ: EnumToken.AttrEndTokenType; } @@ -1523,7 +1523,7 @@ export declare interface AttrEndToken extends BaseToken { */ export declare interface ParensStartToken extends BaseToken { /** - * parenthesis start type + * @inheritdoc */ typ: EnumToken.StartParensTokenType; } @@ -1533,7 +1533,7 @@ export declare interface ParensStartToken extends BaseToken { */ export declare interface ParensEndToken extends BaseToken { /** - * parenthesis end type + * @inheritdoc */ typ: EnumToken.EndParensTokenType; } @@ -1543,7 +1543,7 @@ export declare interface ParensEndToken extends BaseToken { */ export declare interface ParensToken extends BaseToken { /** - * parenthesis type + * @inheritdoc */ typ: EnumToken.ParensTokenType; /** @@ -1557,7 +1557,7 @@ export declare interface ParensToken extends BaseToken { */ export declare interface WhitespaceToken extends BaseToken { /** - * whitespace type + * @inheritdoc */ typ: EnumToken.WhitespaceTokenType; /** @@ -1571,7 +1571,7 @@ export declare interface WhitespaceToken extends BaseToken { */ export declare interface CommentToken extends BaseToken { /** - * comment type + * @inheritdoc */ typ: EnumToken.CommentTokenType; /** @@ -1585,7 +1585,7 @@ export declare interface CommentToken extends BaseToken { */ export declare interface BadCommentToken extends BaseToken { /** - * bad comment type + * @inheritdoc */ typ: EnumToken.BadCommentTokenType; /** @@ -1598,7 +1598,13 @@ export declare interface BadCommentToken extends BaseToken { * CDO comment token */ export declare interface CDOCommentToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.CDOCOMMTokenType; + /** + * CDO comment value + */ val: string; } @@ -1607,7 +1613,7 @@ export declare interface CDOCommentToken extends BaseToken { */ export declare interface BadCDOCommentToken extends BaseToken { /** - * bad CDO comment type + * @inheritdoc */ typ: EnumToken.BadCdoTokenType; /** @@ -1621,7 +1627,7 @@ export declare interface BadCDOCommentToken extends BaseToken { */ export declare interface IncludeMatchToken extends BaseToken { /** - * include match type + * @inheritdoc */ typ: EnumToken.IncludeMatchTokenType; // val: '~='; @@ -1632,7 +1638,7 @@ export declare interface IncludeMatchToken extends BaseToken { */ export declare interface DashMatchToken extends BaseToken { /** - * dash match type + * @inheritdoc */ typ: EnumToken.DashMatchTokenType; // val: '|='; @@ -1643,7 +1649,7 @@ export declare interface DashMatchToken extends BaseToken { */ export declare interface EqualMatchToken extends BaseToken { /** - * equal match type + * @inheritdoc */ typ: EnumToken.EqualMatchTokenType; // val: '|='; @@ -1654,7 +1660,7 @@ export declare interface EqualMatchToken extends BaseToken { */ export declare interface StartMatchToken extends BaseToken { /** - * start match type + * @inheritdoc */ typ: EnumToken.StartMatchTokenType; // val: '^='; @@ -1665,7 +1671,7 @@ export declare interface StartMatchToken extends BaseToken { */ export declare interface EndMatchToken extends BaseToken { /** - * end match type + * @inheritdoc */ typ: EnumToken.EndMatchTokenType; // val: '|='; @@ -1676,7 +1682,7 @@ export declare interface EndMatchToken extends BaseToken { */ export declare interface ContainMatchToken extends BaseToken { /** - * contain match type + * @inheritdoc */ typ: EnumToken.ContainMatchTokenType; // val: '|='; @@ -1687,7 +1693,7 @@ export declare interface ContainMatchToken extends BaseToken { */ export declare interface LessThanToken extends BaseToken { /** - * less than type + * @inheritdoc */ typ: EnumToken.LtTokenType; } @@ -1697,7 +1703,7 @@ export declare interface LessThanToken extends BaseToken { */ export declare interface LessThanOrEqualToken extends BaseToken { /** - * less than or equal type + * @inheritdoc */ typ: EnumToken.LteTokenType; } @@ -1707,7 +1713,7 @@ export declare interface LessThanOrEqualToken extends BaseToken { */ export declare interface GreaterThanToken extends BaseToken { /** - * greater than type + * @inheritdoc */ typ: EnumToken.GtTokenType; } @@ -1717,7 +1723,7 @@ export declare interface GreaterThanToken extends BaseToken { */ export declare interface GreaterThanOrEqualToken extends BaseToken { /** - * greater than or equal type + * @inheritdoc */ typ: EnumToken.GteTokenType; } @@ -1727,7 +1733,7 @@ export declare interface GreaterThanOrEqualToken extends BaseToken { */ export declare interface ColumnCombinatorToken extends BaseToken { /** - * column combinator type + * @inheritdoc */ typ: EnumToken.ColumnCombinatorTokenType; } @@ -1737,7 +1743,7 @@ export declare interface ColumnCombinatorToken extends BaseToken { */ export declare interface PseudoClassToken extends BaseToken { /** - * Pseudo class + * @inheritdoc */ typ: EnumToken.PseudoClassTokenType; /** @@ -1751,7 +1757,7 @@ export declare interface PseudoClassToken extends BaseToken { */ export declare interface PseudoElementToken extends BaseToken { /** - * Pseudo element + * @inheritdoc */ typ: EnumToken.PseudoElementTokenType; /** @@ -1765,7 +1771,7 @@ export declare interface PseudoElementToken extends BaseToken { */ export declare interface PseudoPageToken extends BaseToken { /** - * Pseudo page + * @inheritdoc */ typ: EnumToken.PseudoPageTokenType; /** @@ -1779,7 +1785,7 @@ export declare interface PseudoPageToken extends BaseToken { */ export declare interface PseudoClassFunctionToken extends BaseToken { /** - * Pseudo class function + * @inheritdoc */ typ: EnumToken.PseudoClassFuncTokenType; /** @@ -1797,7 +1803,7 @@ export declare interface PseudoClassFunctionToken extends BaseToken { */ export declare interface DelimToken extends BaseToken { /** - * Delimiter token type + * @inheritdoc */ typ: EnumToken.DelimTokenType; } @@ -1807,7 +1813,7 @@ export declare interface DelimToken extends BaseToken { */ export declare interface BadUrlToken extends BaseToken { /** - * Bad URL + * @inheritdoc */ typ: EnumToken.BadUrlTokenType; /** @@ -1821,7 +1827,7 @@ export declare interface BadUrlToken extends BaseToken { */ export declare interface UrlToken extends BaseToken { /** - * URL + * @inheritdoc */ typ: EnumToken.UrlTokenTokenType; /** @@ -1835,7 +1841,7 @@ export declare interface UrlToken extends BaseToken { */ export declare interface EOFToken extends BaseToken { /** - * End of file + * @inheritdoc */ typ: EnumToken.EOFTokenType; } @@ -1845,7 +1851,7 @@ export declare interface EOFToken extends BaseToken { */ export declare interface ImportantToken extends BaseToken { /** - * Important + * @inheritdoc */ typ: EnumToken.ImportantTokenType; } @@ -1855,7 +1861,7 @@ export declare interface ImportantToken extends BaseToken { */ export declare interface ColorToken extends BaseToken { /** - * Color type + * @inheritdoc */ typ: EnumToken.ColorTokenType; /** @@ -1881,7 +1887,7 @@ export declare interface ColorToken extends BaseToken { */ export declare interface AttrToken extends BaseToken { /** - * Attribute type + * @inheritdoc */ typ: EnumToken.AttrTokenType; /** @@ -1895,7 +1901,7 @@ export declare interface AttrToken extends BaseToken { */ export declare interface InvalidAttrToken extends BaseToken { /** - * Attribute type + * @inheritdoc */ typ: EnumToken.InvalidAttrTokenType; /** @@ -1908,6 +1914,9 @@ export declare interface InvalidAttrToken extends BaseToken { * Child combinator token */ export declare interface ChildCombinatorToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.ChildCombinatorTokenType; } @@ -1916,7 +1925,7 @@ export declare interface ChildCombinatorToken extends BaseToken { */ export declare interface MediaFeatureToken extends BaseToken { /** - * Media feature type + * @inheritdoc */ typ: EnumToken.MediaFeatureTokenType; /** @@ -1930,7 +1939,7 @@ export declare interface MediaFeatureToken extends BaseToken { */ export declare interface NotToken extends BaseToken { /** - * Media feature not type + * @inheritdoc */ typ: EnumToken.NotTokenType; /** @@ -1944,7 +1953,7 @@ export declare interface NotToken extends BaseToken { */ export declare interface MediaFeatureOnlyToken extends BaseToken { /** - * Media feature only type + * @inheritdoc */ typ: EnumToken.OnlyTokenType; /** @@ -1958,7 +1967,7 @@ export declare interface MediaFeatureOnlyToken extends BaseToken { */ export declare interface AndToken extends BaseToken { /** - * Media feature and type + * @inheritdoc */ typ: EnumToken.AndTokenType; } @@ -1968,7 +1977,7 @@ export declare interface AndToken extends BaseToken { */ export declare interface OrToken extends BaseToken { /** - * Media feature or type + * @inheritdoc */ typ: EnumToken.OrTokenType; } @@ -1978,7 +1987,7 @@ export declare interface OrToken extends BaseToken { */ export declare interface MediaQueryUnaryFeatureToken extends BaseToken { /** - * Media query condition type + * @inheritdoc */ typ: EnumToken.MediaQueryUnaryFeatureTokenType; /** @@ -1993,7 +2002,7 @@ export declare interface MediaQueryUnaryFeatureToken extends BaseToken { export declare interface SupportsQueryUnaryConditionToken extends BaseToken { /** - * Supports query condition type + * @inheritdoc */ typ: EnumToken.SupportsQueryUnaryConditionTokenType; /** @@ -2008,7 +2017,7 @@ export declare interface SupportsQueryUnaryConditionToken extends BaseToken { export declare interface SupportsQueryConditionToken extends BaseToken { /** - * Supports query condition type + * @inheritdoc */ typ: EnumToken.SupportsQueryConditionTokenType; /** @@ -2027,7 +2036,7 @@ export declare interface SupportsQueryConditionToken extends BaseToken { export declare interface WhenElseQueryConditionToken extends BaseToken { /** - * When else query condition type + * @inheritdoc */ typ: EnumToken.WhenElseQueryConditionTokenType; /** @@ -2046,7 +2055,7 @@ export declare interface WhenElseQueryConditionToken extends BaseToken { export declare interface WhenElseUnaryConditionToken extends BaseToken { /** - * When else query condition type + * @inheritdoc */ typ: EnumToken.WhenElseUnaryConditionTokenType; /** @@ -2061,7 +2070,7 @@ export declare interface WhenElseUnaryConditionToken extends BaseToken { export declare interface MediaQueryConditionToken extends BaseToken { /** - * Media query condition type + * @inheritdoc */ typ: EnumToken.MediaQueryConditionTokenType; /** @@ -2088,7 +2097,7 @@ export declare interface MediaQueryConditionToken extends BaseToken { export declare interface IfConditionToken extends BaseToken { /** - * If condition type + * @inheritdoc */ typ: EnumToken.IfConditionTokenType; /** @@ -2103,7 +2112,7 @@ export declare interface IfConditionToken extends BaseToken { export declare interface IfElseConditionToken extends BaseToken { /** - * If else condition type + * @inheritdoc */ typ: EnumToken.IfElseConditionTokenType; /** @@ -2117,23 +2126,67 @@ export declare interface IfElseConditionToken extends BaseToken { } export declare interface ContainerStyleRangeToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.ContainerStyleRangeTokenType; + /** + * condition left handle + */ l: Token$1[]; + /** + * condition operator + */ op: Token$1[]; + /** + * condition value + */ r: Token$1[]; } +// (20px <= width < 30px) +/** + * @inheritdoc + */ export declare interface MediaRangeQueryToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.MediaRangeQueryTokenType; + /** + * left hanle + * */ l: Token$1[]; + /** + * media feature name + */ val: Token$1[]; + /** + * first comparator + */ op1: LessThanToken | GreaterThanToken | LessThanOrEqualToken | GreaterThanOrEqualToken; + /** + * second comparator + */ op2: LessThanToken | GreaterThanToken | LessThanOrEqualToken | GreaterThanOrEqualToken; + /** + * right handle + */ r: Token$1[]; } +/** + * @inheritdoc + */ export declare interface InvalidMediaQueryToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.InvalidMediaQueryTokenType; + + /** + * children + */ chi: Token$1[]; } @@ -2141,6 +2194,9 @@ export declare interface InvalidMediaQueryToken extends BaseToken { * Descendant combinator token */ export declare interface DescendantCombinatorToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.DescendantCombinatorTokenType; } @@ -2148,6 +2204,9 @@ export declare interface DescendantCombinatorToken extends BaseToken { * Next sibling combinator token */ export declare interface NextSiblingCombinatorToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.NextSiblingCombinatorTokenType; } @@ -2155,6 +2214,9 @@ export declare interface NextSiblingCombinatorToken extends BaseToken { * Subsequent sibling combinator token */ export declare interface SubsequentCombinatorToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.SubsequentSiblingCombinatorTokenType; } @@ -2162,6 +2224,9 @@ export declare interface SubsequentCombinatorToken extends BaseToken { * Add token */ export declare interface AddToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.Add; } @@ -2169,6 +2234,9 @@ export declare interface AddToken extends BaseToken { * Sub token */ export declare interface SubToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.Sub; } @@ -2176,6 +2244,9 @@ export declare interface SubToken extends BaseToken { * Div token */ export declare interface DivToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.Div; } @@ -2184,7 +2255,7 @@ export declare interface DivToken extends BaseToken { */ export declare interface MulToken extends BaseToken { /** - * Type + * @inheritdoc */ typ: EnumToken.Mul; } @@ -2194,7 +2265,7 @@ export declare interface MulToken extends BaseToken { */ export declare interface WrappedValuesToken extends BaseToken { /** - * Type + * @inheritdoc */ typ: EnumToken.WrappedValuesTokenType; /** @@ -2208,7 +2279,7 @@ export declare interface WrappedValuesToken extends BaseToken { */ export declare interface UnaryExpression extends BaseToken { /** - * Type + * @inheritdoc */ typ: EnumToken.UnaryExpressionTokenType; /** @@ -2225,8 +2296,17 @@ export declare interface UnaryExpression extends BaseToken { * Fraction token */ export declare interface FractionToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.FractionTokenType; + /** + * Left handle + */ l: NumberToken; + /** + * Right handle + */ r: NumberToken; } @@ -2234,9 +2314,21 @@ export declare interface FractionToken extends BaseToken { * Binary expression token */ export declare interface BinaryExpressionToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.BinaryExpressionTokenType; + /** + * Operator + */ op: EnumToken.Add | EnumToken.Sub | EnumToken.Div | EnumToken.Mul; + /** + * Left handle + */ l: BinaryExpressionNode | Token$1; + /** + * Right handle + */ r: BinaryExpressionNode | Token$1; } @@ -2244,10 +2336,25 @@ export declare interface BinaryExpressionToken extends BaseToken { * Match expression token */ export declare interface MatchExpressionToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.MatchExpressionTokenType; + /** + * Operator + */ op: EqualMatchToken | DashMatchToken | StartMatchToken | ContainMatchToken | EndMatchToken | IncludeMatchToken; + /** + * Left handle + */ l: Token$1; + /** + * Right handle + */ r: Token$1; + /** + * Flags + */ attr?: "i" | "s"; } @@ -2255,8 +2362,17 @@ export declare interface MatchExpressionToken extends BaseToken { * Name space attribute token */ export declare interface NameSpaceAttributeToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.NameSpaceAttributeTokenType; + /** + * Left handle + */ l?: Token$1; + /** + * Right handle + */ r: Token$1; } @@ -2264,7 +2380,13 @@ export declare interface NameSpaceAttributeToken extends BaseToken { * List token */ export declare interface ListToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.ListToken; + /** + * Children + */ chi: Token$1[]; } @@ -2272,8 +2394,17 @@ export declare interface ListToken extends BaseToken { * Composes selector token */ export declare interface ComposesSelectorToken extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.ComposesSelectorTokenType; + /** + * Left handle + */ l: Token$1[]; + /** + * Right handle + */ r: Token$1 | null; } @@ -2281,20 +2412,53 @@ export declare interface ComposesSelectorToken extends BaseToken { * Css variable token */ export declare interface CssVariableToken$1 extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.CssVariableTokenType; + /** + * Name + */ nam: string; + /** + * Value + */ val: Token$1[]; } +/** + * Css variable import token + */ export declare interface CssVariableImportTokenType$1 extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.CssVariableImportTokenType; + /** + * Name + */ nam: string; + /** + * Value + */ val: Token$1[]; } +/** + * Css variable map token + */ export declare interface CssVariableMapTokenType extends BaseToken { + /** + * @inheritdoc + */ typ: EnumToken.CssVariableDeclarationMapTokenType; + /** + * CSS Variables + */ vars: Token$1[]; + /** + * From clause + */ from: Token$1[]; } @@ -2302,6 +2466,9 @@ export declare interface CssVariableMapTokenType extends BaseToken { * Function definition token */ export declare interface FunctionDefToken extends BaseToken { + /** + * @inheritdoc + */ typ: | EnumToken.FunctionDefTokenType | EnumToken.UrlFunctionTokenDefType @@ -2313,7 +2480,13 @@ export declare interface FunctionDefToken extends BaseToken { | EnumToken.MathFunctionTokenDefType | EnumToken.PseudoClassFunctionTokenDefType | EnumToken.TransformFunctionTokenDefType; + /** + * Name + */ nam: string; + /** + * Value + */ val: string; } @@ -2321,7 +2494,13 @@ export declare interface FunctionDefToken extends BaseToken { * Raw node token */ export declare interface RawNodeToken extends BaseToken, EnumAstNodeStatus$1 { + /** + * @inheritdoc + */ typ: EnumToken.RawNodeTokenType; + /** + * Value + */ val: Token$1[]; } @@ -2534,7 +2713,7 @@ export declare interface BaseToken { /** * parent node */ - parent?: AstAtRule | astRule | AstKeyframesAtRule | AstKeyFrameRule | AstInvalidRule | AstInvalidAtRule | null; + parent?: AstAtRule | astRule | AstKeyframesAtRule | AstKeyframesRule | AstInvalidRule | AstInvalidAtRule | null; /** * @private */ @@ -2676,36 +2855,6 @@ export declare interface AstInvalidAtRule extends BaseToken, AstNodeStatus { chi?: Array; } -/** - * keyframe rule node - */ -export declare interface AstKeyFrameRule extends BaseToken, AstNodeStatus { - /** - * token type - */ - typ: EnumToken.KeyFramesRuleNodeType; - /** - * selector - */ - sel: string; - /** - * child nodes - */ - chi: Array; - /** - * optimized selector - */ - optimized?: OptimizedSelector; - /** - * raw selector - */ - raw?: RawSelectorTokens; - /** - * tokens - */ - tokens?: Token$1[]; -} - /** * raw selector tokens */ @@ -2781,6 +2930,36 @@ export declare interface AstAtRule extends BaseToken, AstNodeStatus { chi?: Array; } +/** + * keyframe rule node + */ +export declare interface AstKeyframesRule extends BaseToken, AstNodeStatus { + /** + * token type + */ + typ: EnumToken.KeyFramesRuleNodeType; + /** + * selector + */ + sel: string; + /** + * child nodes + */ + chi: Array; + /** + * optimized selector + */ + optimized?: OptimizedSelector; + /** + * raw selector + */ + raw?: RawSelectorTokens; + /** + * tokens + */ + tokens?: Token$1[]; +} + /** * keyframe rule node */ @@ -2837,7 +3016,7 @@ export declare type AstRuleList = | AstAtRule | AstRule | AstKeyframesAtRule - | AstKeyFrameRule + | AstKeyframesRule | AstInvalidRule; /** @@ -2865,7 +3044,7 @@ export declare type AstNode$1 = | AstRule | AstDeclaration | AstKeyframesAtRule - | AstKeyFrameRule + | AstKeyframesRule | AstInvalidRule | AstInvalidAtRule | AstInvalidDeclaration @@ -3060,24 +3239,40 @@ declare function walkValues(values: Token$1[], root?: AstNode$1 | Token$1 | null type?: EnumToken | EnumToken[] | ((token: Token$1) => boolean); }, reverse?: boolean): Generator; +/** + * Generic visitor result + */ export declare type GenericVisitorSyncResult = T | T[] | null; -export declare type GenericVisitorAsyncResult = Promise | Promise| Promise; +/** + * Generic visitor result + */ +export declare type GenericVisitorAsyncResult = Promise | Promise | Promise; +/** + * Generic visitor result + */ export declare type GenericVisitorResult = GenericVisitorSyncResult | GenericVisitorAsyncResult; - - +/** + * Generic visitor handler + */ export declare type GenericVisitorSyncHandler = ( node: T, parent?: AstNode | Token, root?: AstNode | Token, ) => GenericVisitorSyncResult; +/** + * Generic visitor handler + */ export declare type GenericVisitorAstNodeSyncHandlerMap = | Record> | GenericVisitorSyncHandler | { type: WalkerEvent; handler: GenericVisitorSyncHandler } | { type: WalkerEvent; handler: Record> }; +/** + * Generic visitor handler + */ export declare type ValueVisitorSyncHandler = GenericVisitorSyncHandler; /** @@ -3273,8 +3468,14 @@ export declare interface VisitorSyncNodeMap { */ Rule?: GenericVisitorAstNodeSyncHandlerMap; + /** + * keyframes rule visitor + */ KeyframesRule?: GenericVisitorAstNodeSyncHandlerMap; + /** + * keyframes at-rule visitor + */ KeyframesAtRule?: GenericVisitorAstNodeSyncHandlerMap; /** @@ -3329,22 +3530,32 @@ export declare interface VisitorSyncNodeMap { * // body {color:#f3fff0} * ``` */ - [key: keyof typeof EnumToken]: GenericVisitorAstNodeSyncHandlerMap | GenericVisitorAstNodeSyncHandlerMap; + [key: keyof typeof EnumToken]: + | GenericVisitorAstNodeSyncHandlerMap + | GenericVisitorAstNodeSyncHandlerMap; } - +/** + * Generic visitor handler + */ export declare type GenericVisitorHandler = ( node: T, parent?: AstNode | Token, root?: AstNode | Token, ) => GenericVisitorSyncResult | GenericVisitorAsyncResult; +/** + * Generic visitor handler + */ export declare type GenericVisitorAstNodeHandlerMap = | Record> | GenericVisitorHandler | { type: WalkerEvent; handler: GenericVisitorHandler } | { type: WalkerEvent; handler: Record> }; +/** + * Generic visitor handler + */ export declare type ValueVisitorHandler = GenericVisitorHandler; /** @@ -4645,19 +4856,55 @@ export declare type WalkerValueFilter = ( parents?: Generator, ) => WalkerOption | null; +/** + * walker result + */ export declare interface WalkResult { + /** + * current node + */ node: AstNode$1; + /** + * parent node + */ parent?: AstRuleList; + /** + * root node + */ root?: AstNode$1; + /** + * parent nodes + */ parents: Generator; } +/** + * walker result + */ export declare interface WalkAttributesResult { + /** + * current node + */ value: Token$1; + /** + * previous node + */ previousValue: Token$1 | null; + /** + * next node + */ nextValue: Token$1 | null; + /** + * root node + */ root?: AstNode$1 | Token$1 | null; + /** + * parent node + */ parent: AstNode$1 | Token$1 | null; + /** + * parent nodes + */ parents: Generator; } @@ -5092,10 +5339,21 @@ export declare interface ParseInputStreamOptions { * @internal */ export declare interface ParseSourceOptions { + /** + * Source file to be used for sourcemap + * @internal + */ sourcesMap?: Map; + /** + * Source file to be used for sourcemap + * @internal + */ source?: SourceFile | null; } +/** + * Parser sourcemap options + */ export declare interface ParserSourceMapOptions { /** * Include sourcemap in the ast. Sourcemap info is always generated @@ -5107,6 +5365,9 @@ export declare interface ParserSourceMapOptions { inputSourceMap?: SourceMapObject | string; } +/** + * Sync parseroptions + */ export declare interface ParserSyncOptions extends MinifyOptions, @@ -5734,69 +5995,108 @@ declare enum ResponseType$1 { ArrayBuffer = 2 } +/** + * Validation syntax + * @internal + */ export declare interface ValidationSyntaxNode { + /** + * mdn data syntax + */ syntax: string; + /** + * validation tokens + */ ast?: ValidationToken[]; + /** + * descriptors + */ descriptors?: Record>; } +/** + * Validation selector options + * @internal + */ interface ValidationSelectorOptions extends ValidationOptions { + /** + * nested selector + */ nestedSelector?: boolean; } +/** + * Validation media feature + * @internal + */ export declare interface ValidationMediaFeature { + /** + * media feature type + */ type: MediaFeatureType; + /** + * media feature status + */ status?: string; + /** + * media feature category + */ category: string; + /** + * media feature values + */ values?: Array | Array; } +/** + * Validation configuration + * @internal + */ export declare type ValidationConfiguration = Record< ValidationSyntaxGroupEnum, ValidationSyntaxNode | Record | Record >; +/** + * Validation result + * @internal + */ interface ValidationResult { + /** + * validation result + */ valid: SyntaxValidationResult; + /** + * node + */ node: AstNode$1 | Token$1 | null; + /** + * syntax + */ syntax: ValidationToken | string | null; + /** + * error + */ error: string; + /** + * cycle + */ cycle?: boolean; } +/** + * Validation syntax result + * @internal + */ interface ValidationSyntaxResult extends ValidationResult { + /** + * syntax + */ syntax: ValidationToken | string | null; - context: Context | Token$1[]; -} - -interface Context { - index: number; - /** - * The length of the context tokens to be consumed + * context */ - - readonly length: number; - - current(): Type | null; - - update(context: Context): void; - - consume(token: Type, howMany?: number): boolean; - - peek(): Type | null; - - // tokens(): Type[]; - - next(): Type | null; - - consume(token: Type, howMany?: number): boolean; - - slice(): Type[]; - - clone(): Context; - - done(): boolean; + context: ValidationContext | Token$1[]; } /** @@ -5937,6 +6237,11 @@ button { */ declare function find(ast: AstNode$1, matcher: (node: AstNode$1, parent?: AstNode$1 | null) => boolean): AstNode$1 | null; /** + * + * @param ast + * @param matcher + * @returns + * * Search the ast sub-tree by checking each node's value token and return the first match * ```ts @@ -5962,10 +6267,6 @@ button { console.log({node, value}); ``` - * - * @param ast - * @param matcher - * @returns */ declare function findByValue(ast: AstNode$1, matcher: AstValueMatcher): { node: AstNode$1; @@ -6450,4 +6751,4 @@ declare function transform(options: ParseInputStreamOptions & TransformOptions): declare function transform(options: ParseInputFileOptions & TransformOptions): Promise; export { ColorType$1 as ColorType, EnumAstNodeStatus$1 as EnumAstNodeStatus, EnumToken, FeatureWalkMode, ModuleCaseTransformEnum, ModuleScopeEnumOptions, ResponseType$1 as ResponseType, SourceMap, ValidationLevel, WalkerEvent, WalkerOptionEnum, cloneNode, convertColor, dirname, expand, find, findAll, findByValue, findLast, isOkLabClose, load, minify, okLabDistance, parse, parseDeclarations, parseFile, parseString, parseSync, render, renderValue as renderToken, replaceNodeOrValue, resolve, transform, transformFile, transformSync, walk, walkValues }; -export type { AddToken, AndToken, AngleToken, AstAtRule, AstComment, AstDeclaration, AstInvalidAtRule, AstInvalidDeclaration, AstInvalidRule, AstKeyFrameRule, AstKeyframesAtRule, AstKeyframesRule, AstNode$1 as AstNode, AstNodeStatus, AstRule, AstRuleList, AstStyleSheet, AstValueMatcher, AtRuleToken, AtRuleVisitorHandler, AttrEndToken, AttrStartToken, AttrToken, Background, BackgroundAttachmentMapping, BackgroundPosition, BackgroundPositionClass, BackgroundPositionConstraints, BackgroundPositionMapping, BackgroundProperties, BackgroundRepeat, BackgroundRepeatMapping, BackgroundSize, BackgroundSizeMapping, BadCDOCommentToken, BadCommentToken, BadStringToken, BadUrlToken, BaseToken, BinaryExpressionNode, BinaryExpressionToken, BlockEndToken, BlockStartToken, Border, BorderColor, BorderColorClass, BorderProperties, BorderRadius, CDOCommentToken, ChildCombinatorToken, ClassSelectorToken, ColonToken, ColorToken, ColumnCombinatorToken, CommaToken, CommentToken, ComposesSelectorToken, ConstraintsMapping, ContainMatchToken, ContainerStyleRangeToken, Context, CssVariableImportTokenType$1 as CssVariableImportTokenType, CssVariableMapTokenType, CssVariableToken$1 as CssVariableToken, DashMatchToken, DashedIdentToken, DeclarationVisitorHandler, DelimToken, DescendantCombinatorToken, DimensionToken, DivToken, DoubleColonToken, EOFToken, EndMatchToken, EqualMatchToken, ErrorDescription$1 as ErrorDescription, FlexToken, Font, FontFamily, FontProperties, FontWeight, FontWeightConstraints, FontWeightMapping, FractionToken, FrequencyToken, FunctionDefToken, FunctionImageToken, FunctionToken, FunctionURLToken, GenericVisitorAstNodeHandlerMap, GenericVisitorAstNodeSyncHandlerMap, GenericVisitorAsyncResult, GenericVisitorHandler, GenericVisitorResult, GenericVisitorSyncHandler, GenericVisitorSyncResult, GreaterThanOrEqualToken, GreaterThanToken, GridTemplateFuncToken, HashToken, IdentListToken, IdentToken, IfConditionToken, IfElseConditionToken, ImportantToken, IncludeMatchToken, InvalidAttrToken, InvalidClassSelectorToken, InvalidMediaQueryToken, LengthToken, LessThanOrEqualToken, LessThanToken, LineHeight, ListToken, LiteralToken, LoadResult, Map$1 as Map, MatchExpressionToken, MatchedSelector, MediaFeatureOnlyToken, MediaFeatureToken, MediaQueryConditionToken, MediaQueryUnaryFeatureToken, MediaRangeQueryToken, MinifyFeature, MinifyFeatureOptions, MinifyOptions, ModuleAsyncOptions, ModuleSyncOptions, MulToken, NameSpaceAttributeToken, NestingSelectorToken, NextSiblingCombinatorToken, NotToken, NumberToken, OptimizedSelector, OptimizedSelectorToken, OrToken, Outline, OutlineProperties, ParensEndToken, ParensStartToken, ParensToken, ParseInfo$1 as ParseInfo, ParseInputFileOptions, ParseInputOptions, ParseInputStreamOptions, ParseResult, ParseResultStats, ParseSourceOptions, ParseTokenOptions, ParserOptions, ParserSourceMapOptions, ParserSyncOptions, PercentageToken, Prefix, PropertiesConfig, PropertiesConfigProperties, PropertyListOptions, PropertyMapType, PropertySetType, PropertyType, PseudoClassFunctionToken, PseudoClassToken, PseudoElementToken, PseudoPageToken, PurpleBackgroundAttachment, RawNodeToken, RawSelectorTokens, RenderOptions, RenderResult, ResolutionToken, ResolvedPath, RuleVisitorHandler, SemiColonToken, Separator, ShorthandDef, ShorthandMapType, ShorthandProperties, ShorthandPropertyType, ShorthandType, SinglePropertyType, SinglePropertyTypeMapping, SourceLocation, SourceMapObject, StartMatchToken, StringToken, SubToken, SubsequentCombinatorToken, SupportsQueryConditionToken, SupportsQueryUnaryConditionToken, TimeToken, TimelineFunctionToken, TimingFunctionToken, Token$1 as Token, TokenSearchResult, TokenizeResult, TransformOptions, TransformResult, TransformSyncOptions, UnaryExpression, UnaryExpressionNode, UnclosedStringToken, UniversalSelectorToken, UrlToken, ValidationConfiguration, ValidationMediaFeature, ValidationOptions, ValidationResult, ValidationSelectorOptions, ValidationSyntaxNode, ValidationSyntaxResult, ValidationToken$1 as ValidationToken, Value, ValueVisitorHandler, ValueVisitorSyncHandler, VariableScopeInfo, VisitorNodeMap, VisitorSyncNodeMap, WalkAttributesResult, WalkResult, WalkerFilter, WalkerOption, WalkerValueFilter, WhenElseQueryConditionToken, WhenElseUnaryConditionToken, WhitespaceToken, WrappedValuesToken }; +export type { AddToken, AndToken, AngleToken, AstAtRule, AstComment, AstDeclaration, AstInvalidAtRule, AstInvalidDeclaration, AstInvalidRule, AstKeyframesAtRule, AstKeyframesRule, AstNode$1 as AstNode, AstNodeStatus, AstRule, AstRuleList, AstStyleSheet, AstValueMatcher, AtRuleToken, AtRuleVisitorHandler, AttrEndToken, AttrStartToken, AttrToken, Background, BackgroundAttachmentMapping, BackgroundPosition, BackgroundPositionClass, BackgroundPositionConstraints, BackgroundPositionMapping, BackgroundProperties, BackgroundRepeat, BackgroundRepeatMapping, BackgroundSize, BackgroundSizeMapping, BadCDOCommentToken, BadCommentToken, BadStringToken, BadUrlToken, BaseToken, BinaryExpressionNode, BinaryExpressionToken, BlockEndToken, BlockStartToken, Border, BorderColor, BorderColorClass, BorderProperties, BorderRadius, CDOCommentToken, ChildCombinatorToken, ClassSelectorToken, ColonToken, ColorToken, ColumnCombinatorToken, CommaToken, CommentToken, ComposesSelectorToken, ConstraintsMapping, ContainMatchToken, ContainerStyleRangeToken, CssVariableImportTokenType$1 as CssVariableImportTokenType, CssVariableMapTokenType, CssVariableToken$1 as CssVariableToken, DashMatchToken, DashedIdentToken, DeclarationVisitorHandler, DelimToken, DescendantCombinatorToken, DimensionToken, DivToken, DoubleColonToken, EOFToken, EndMatchToken, EqualMatchToken, ErrorDescription$1 as ErrorDescription, FlexToken, Font, FontFamily, FontProperties, FontWeight, FontWeightConstraints, FontWeightMapping, FractionToken, FrequencyToken, FunctionDefToken, FunctionImageToken, FunctionToken, FunctionURLToken, GenericVisitorAstNodeHandlerMap, GenericVisitorAstNodeSyncHandlerMap, GenericVisitorAsyncResult, GenericVisitorHandler, GenericVisitorResult, GenericVisitorSyncHandler, GenericVisitorSyncResult, GreaterThanOrEqualToken, GreaterThanToken, GridTemplateFuncToken, HashToken, IdentListToken, IdentToken, IfConditionToken, IfElseConditionToken, ImportantToken, IncludeMatchToken, InvalidAttrToken, InvalidClassSelectorToken, InvalidMediaQueryToken, LengthToken, LessThanOrEqualToken, LessThanToken, LineHeight, ListToken, LiteralToken, LoadResult, Map$1 as Map, MatchExpressionToken, MatchedSelector, MediaFeatureOnlyToken, MediaFeatureToken, MediaQueryConditionToken, MediaQueryUnaryFeatureToken, MediaRangeQueryToken, MinifyFeature, MinifyFeatureOptions, MinifyOptions, ModuleAsyncOptions, ModuleSyncOptions, MulToken, NameSpaceAttributeToken, NestingSelectorToken, NextSiblingCombinatorToken, NotToken, NumberToken, OptimizedSelector, OptimizedSelectorToken, OrToken, Outline, OutlineProperties, ParensEndToken, ParensStartToken, ParensToken, ParseInfo$1 as ParseInfo, ParseInputFileOptions, ParseInputOptions, ParseInputStreamOptions, ParseResult, ParseResultStats, ParseSourceOptions, ParseTokenOptions, ParserOptions, ParserSourceMapOptions, ParserSyncOptions, PercentageToken, Prefix, PropertiesConfig, PropertiesConfigProperties, PropertyListOptions, PropertyMapType, PropertySetType, PropertyType, PseudoClassFunctionToken, PseudoClassToken, PseudoElementToken, PseudoPageToken, PurpleBackgroundAttachment, RawNodeToken, RawSelectorTokens, RenderOptions, RenderResult, ResolutionToken, ResolvedPath, RuleVisitorHandler, SemiColonToken, Separator, ShorthandDef, ShorthandMapType, ShorthandProperties, ShorthandPropertyType, ShorthandType, SinglePropertyType, SinglePropertyTypeMapping, SourceLocation, SourceMapObject, StartMatchToken, StringToken, SubToken, SubsequentCombinatorToken, SupportsQueryConditionToken, SupportsQueryUnaryConditionToken, TimeToken, TimelineFunctionToken, TimingFunctionToken, Token$1 as Token, TokenSearchResult, TokenizeResult, TransformOptions, TransformResult, TransformSyncOptions, UnaryExpression, UnaryExpressionNode, UnclosedStringToken, UniversalSelectorToken, UrlToken, ValidationConfiguration, ValidationMediaFeature, ValidationOptions, ValidationResult, ValidationSelectorOptions, ValidationSyntaxNode, ValidationSyntaxResult, ValidationToken$1 as ValidationToken, Value, ValueVisitorHandler, ValueVisitorSyncHandler, VariableScopeInfo, VisitorNodeMap, VisitorSyncNodeMap, WalkAttributesResult, WalkResult, WalkerFilter, WalkerOption, WalkerValueFilter, WhenElseQueryConditionToken, WhenElseUnaryConditionToken, WhitespaceToken, WrappedValuesToken }; diff --git a/dist/lib/ast/find.js b/dist/lib/ast/find.js index d70ac623..99a59a98 100644 --- a/dist/lib/ast/find.js +++ b/dist/lib/ast/find.js @@ -42,6 +42,11 @@ function find(ast, matcher) { return null; } /** + * + * @param ast + * @param matcher + * @returns + * * Search the ast sub-tree by checking each node's value token and return the first match * ```ts @@ -67,10 +72,6 @@ button { console.log({node, value}); ``` - * - * @param ast - * @param matcher - * @returns */ function findByValue(ast, matcher) { let source; diff --git a/dist/lib/ast/minify.js b/dist/lib/ast/minify.js index 5830747b..896e8ec6 100644 --- a/dist/lib/ast/minify.js +++ b/dist/lib/ast/minify.js @@ -32,29 +32,29 @@ const features = Object.values(index).sort((a, b) => a.ordering - b.ordering); * @param context * @private */ -function minify(ast, opt = {}, recursive = false, errors, nestingContent, context = {}) { +function minify(ast, options = {}, recursive = false, errors, nestingContent, context = {}) { let preprocess = false; let postprocess = false; let parents; let replacement; // @ts-ignore - let { sourcemap, module, ...options } = opt; - if (!("features" in options)) { + let { sourcemap, module, ...options2 } = options; + if (!("features" in options2)) { // @ts-ignore - options = { + options2 = { removeDuplicateDeclarations: true, computeShorthand: true, computeCalcExpression: true, removePrefix: false, features: [], - ...options, + ...options2, }; for (const feature of features) { - feature.register(options); + feature.register(options2); } - options.features.sort((a, b) => a.ordering - b.ordering); + options2.features.sort((a, b) => a.ordering - b.ordering); } - for (const feature of options.features) { + for (const feature of options2.features) { if (feature.processMode & FeatureWalkMode.Pre) { preprocess = true; } @@ -69,7 +69,7 @@ function minify(ast, opt = {}, recursive = false, errors, nestingContent, contex continue; } replacement = parent; - for (const feature of options.features) { + for (const feature of options2.features) { if ((feature.processMode & FeatureWalkMode.Pre) === 0 || (feature.accept != null && !feature.accept.has(parent.typ))) { continue; @@ -79,7 +79,7 @@ function minify(ast, opt = {}, recursive = false, errors, nestingContent, contex ? replacement.sel : replacement.nam); } - const result = feature.run(replacement, options, parent[PARENT] ?? ast, context, FeatureWalkMode.Pre); + const result = feature.run(replacement, options2, parent[PARENT] ?? ast, context, FeatureWalkMode.Pre); if (result != null) { replacement = result; } @@ -98,14 +98,14 @@ function minify(ast, opt = {}, recursive = false, errors, nestingContent, contex } } } - for (const feature of options.features) { + for (const feature of options2.features) { if (feature.processMode & FeatureWalkMode.Pre && "cleanup" in feature) { // @ts-ignore - feature.cleanup(ast, options, context, FeatureWalkMode.Pre); + feature.cleanup(ast, options2, context, FeatureWalkMode.Pre); } } } - doMinify(ast, options, recursive, errors, nestingContent, context); + doMinify(ast, options2, recursive, errors, nestingContent, context); parents = new Set([ast]); for (const parent of parents) { if (parent.typ == EnumToken.CommentTokenType || parent.typ == EnumToken.CDOCOMMTokenType) { @@ -113,12 +113,12 @@ function minify(ast, opt = {}, recursive = false, errors, nestingContent, contex } replacement = parent; if (postprocess) { - for (const feature of options.features) { + for (const feature of options2.features) { if ((feature.processMode & FeatureWalkMode.Post) === 0 || (feature.accept != null && !feature.accept.has(parent.typ))) { continue; } - const result = feature.run(replacement, options, parent[PARENT] ?? ast, context, FeatureWalkMode.Post); + const result = feature.run(replacement, options2, parent[PARENT] ?? ast, context, FeatureWalkMode.Post); if (result != null) { replacement = result; } @@ -139,10 +139,10 @@ function minify(ast, opt = {}, recursive = false, errors, nestingContent, contex } } if (postprocess) { - for (const feature of options.features) { + for (const feature of options2.features) { if (feature.processMode & FeatureWalkMode.Post && "cleanup" in feature) { // @ts-ignore - feature.cleanup(ast, options, context, FeatureWalkMode.Post); + feature.cleanup(ast, options2, context, FeatureWalkMode.Post); } } } diff --git a/dist/lib/validation/match.js b/dist/lib/validation/match.js index dbed9b43..23f06fbc 100644 --- a/dist/lib/validation/match.js +++ b/dist/lib/validation/match.js @@ -10,11 +10,19 @@ import { parseTokens } from '../parser/parse.js'; const config = getSyntaxConfig(); // @ts-expect-error const allValues = config.declarations.all.syntax.split(/[\s|]+/g); +/** + * @type {Array.} + */ const funcTypes = [ ...tokensfuncDefMap.values(), EnumToken.FunctionTokenType, EnumToken.PseudoClassFuncTokenType, ]; +/** + * trim leading and trailing whitespace + * @param tokens + * @returns + */ function trimArray(tokens) { while (tokens[0]?.typ === EnumToken.WhitespaceTokenType) { tokens.shift(); @@ -115,6 +123,11 @@ function isMFValue(featureName, tokens, isMFRange) { success: true, }; } +/** + * create validation context + * @param tokens + * @returns + */ function createValidationContext(tokens) { tokens = trimArray(tokens.filter((t) => t.typ !== EnumToken.CommentTokenType)); if (tokens.at(-1)?.typ === EnumToken.ImportantTokenType) { @@ -278,6 +291,14 @@ function createValidationContext(tokens) { }; return token; } +/** + * match selector syntax + * @param stream + * @param errors + * @param options + * @param nested + * @returns + */ function matchSelectorSyntax(stream, errors, options, nested = true) { const stack = []; const tokens = []; @@ -716,6 +737,13 @@ function matchSelectorSyntax(stream, errors, options, nested = true) { stream.push(...tokens); return { success, errors }; } +/** + * matches all syntaxes + * @param syntaxes + * @param context + * @param options + * @returns + */ function matchAllSyntaxes(syntaxes, context, options) { const result = matchSyntax(syntaxes, context, { ...options, @@ -754,6 +782,13 @@ function matchAllSyntaxes(syntaxes, context, options) { syntaxToken: !result.success ? result.syntaxToken : null, }; } +/** + * matches a list of syntaxes + * @param syntax + * @param context + * @param options + * @returns + */ function matchListSyntax(syntax, context, options) { const { isList, match, isOptional, ...rest } = syntax; let success = true; @@ -798,6 +833,13 @@ function matchListSyntax(syntax, context, options) { token: context.peek(), }; } +/** + * matches a list of syntaxes + * @param syntax + * @param context + * @param options + * @returns + */ function matchOccurenceSyntax(syntax, context, options) { const { match, ...rest } = syntax; let result = null; @@ -839,6 +881,13 @@ function matchOccurenceSyntax(syntax, context, options) { } return result; } +/** + * matches a list of syntaxes + * @param syntaxes + * @param context + * @param options + * @returns + */ function matchSyntax(syntaxes, context, options) { if (syntaxes == null) { return { @@ -1479,6 +1528,13 @@ function matchSyntax(syntaxes, context, options) { errors: [], }; } +/** + * matches a column of syntaxes + * @param syntax + * @param context + * @param options + * @returns + */ function matchColumnSyntax(syntax, context, options) { let syntaxes = syntax.chi.slice(); let i = 0; @@ -1515,6 +1571,13 @@ function matchColumnSyntax(syntax, context, options) { errors: [], }; } +/** + * matches an ampersand of syntaxes + * @param syntax + * @param context + * @param options + * @returns + */ function matchAmpersandSyntax(syntax, context, options) { const syntaxes = [syntax.l, syntax.r]; let result; @@ -1533,6 +1596,13 @@ function matchAmpersandSyntax(syntax, context, options) { } return result; } +/** + * matches a property + * @param property + * @param context + * @param options + * @returns + */ function matchProperty(property, context, options) { let success = false; let t = context.peek()?.typ; @@ -2265,6 +2335,13 @@ function matchProperty(property, context, options) { errors: [], }; } +/** + * matches a repeatable syntax + * @param syntax + * @param context + * @param options + * @returns + */ function matchRepeatableSyntax(syntax, context, options) { const { isRepeatable, isOptional, isMandatatoryGroup, isRepeatableAtLeastOnce, ...rest } = syntax; let result = null; diff --git a/src/@types/ast.d.ts b/src/@types/ast.d.ts index 5a577a90..1f5efeb3 100644 --- a/src/@types/ast.d.ts +++ b/src/@types/ast.d.ts @@ -1,7 +1,6 @@ import { EnumToken } from "../lib/ast/types.ts"; import { ERRORS, LOC, OPTIMIZED, PARENT, RAW, ROOT, STATE, TOKENS } from "../lib/syntax/constants.ts"; import type { Token } from "./token.d.ts"; -import type { AstNode } from "./ast.d.ts"; /** * token or node location diff --git a/src/@types/index.d.ts b/src/@types/index.d.ts index 70fcdea3..9e323ba4 100644 --- a/src/@types/index.d.ts +++ b/src/@types/index.d.ts @@ -7,7 +7,6 @@ import type { CssVariableToken, Token } from "./token.d.ts"; import { FeatureWalkMode } from "../lib/ast/features/type.ts"; import { ValidationToken } from "../lib/validation/parser/types"; import { SourceFile } from "../lib/parser/source.ts"; -import type { VisitorSyncNodeMap, VisitorNodeMap } from "./visitor.d.ts"; export * from "./ast.d.ts"; export * from "./token.d.ts"; From 75cd05f851c213f616216264780fec4c3a3d0c55 Mon Sep 17 00:00:00 2001 From: Thierry Bela Nanga Date: Sun, 16 Aug 2026 22:27:06 -0400 Subject: [PATCH 08/22] remove sourcemap flag #146 --- test/allFiles.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/allFiles.js b/test/allFiles.js index 841590ef..184efb15 100644 --- a/test/allFiles.js +++ b/test/allFiles.js @@ -20,11 +20,11 @@ for (const file of await readdir(baseDir)) { message = ''; const result = await load(baseDir + file, import.meta.dirname).then(css => transform(css, { - src: baseDir + file, minify: true, sourcemap: true, + src: baseDir + file, minify: true, removePrefix: true, nestingRules: true, resolveImport: true, - sourcemap: true, + // sourcemap: true, validation: true })); From c7b46acb88c219c78d7fdc9a3f53e668a6e78a95 Mon Sep 17 00:00:00 2001 From: Thierry Bela Nanga Date: Sun, 16 Aug 2026 22:29:50 -0400 Subject: [PATCH 09/22] remove sourcemap flag #146 --- test/allFiles.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/allFiles.js b/test/allFiles.js index 184efb15..e82da112 100644 --- a/test/allFiles.js +++ b/test/allFiles.js @@ -31,7 +31,7 @@ for (const file of await readdir(baseDir)) { message += `[inputSize]: ${toFileSize(result.stats.bytesIn)}\n `; message += `[outputSize]: ${toFileSize(result.stats.bytesOut)}\n `; message += `[ratio]: ${(100 * (1 - result.stats.bytesOut / result.stats.bytesIn)).toFixed(2)}%\n `; - message += `[sourcemap]: ${JSON.stringify(result.map.toJSON()).length}\n `; + // message += `[sourcemap]: ${JSON.stringify(result.map.toJSON()).length}\n `; for (const key in result.stats) { From 2628ebcef9e1c9034e16af85a65f78a0e2fb3a97 Mon Sep 17 00:00:00 2001 From: Thierry Bela Nanga Date: Mon, 17 Aug 2026 02:53:39 -0400 Subject: [PATCH 10/22] rewrite visitors handling #146 --- dist/index-umd-web.js | 1007 +++++++++------------ dist/index.cjs | 1007 +++++++++------------ dist/index.d.ts | 6 +- dist/lib/ast/features/shorthand.js | 2 +- dist/lib/ast/features/transform.js | 2 +- dist/lib/ast/minify.js | 12 +- dist/lib/ast/types.js | 2 +- dist/lib/parser/parse.js | 993 +++++++++------------ dist/lib/parser/utils/selector.js | 2 +- dist/lib/renderer/render.js | 4 +- src/@types/ast.d.ts | 4 +- src/lib/ast/features/shorthand.ts | 2 +- src/lib/ast/features/transform.ts | 2 +- src/lib/ast/find.ts | 2 +- src/lib/ast/minify.ts | 12 +- src/lib/ast/types.ts | 2 +- src/lib/parser/parse.ts | 1309 ++++++++++++---------------- src/lib/parser/utils/selector.ts | 2 +- src/lib/renderer/render.ts | 4 +- test/specs/code/modules.js | 34 +- test/specs/code/visitors.js | 390 +++++++-- 21 files changed, 2215 insertions(+), 2585 deletions(-) diff --git a/dist/index-umd-web.js b/dist/index-umd-web.js index 768c4117..ffe67ee0 100644 --- a/dist/index-umd-web.js +++ b/dist/index-umd-web.js @@ -391,7 +391,7 @@ /** * keyframe rule node type */ - EnumToken[EnumToken["KeyFramesRuleNodeType"] = 73] = "KeyFramesRuleNodeType"; + EnumToken[EnumToken["KeyframesRuleNodeType"] = 73] = "KeyframesRuleNodeType"; /** * class selector token type */ @@ -19719,7 +19719,7 @@ accept = new Set([ exports.EnumToken.RuleNodeType, exports.EnumToken.AtRuleNodeType, - exports.EnumToken.KeyFramesRuleNodeType, + exports.EnumToken.KeyframesRuleNodeType, ]); get ordering() { return 10; @@ -21060,7 +21060,7 @@ } class TransformCssFeature { - accept = new Set([exports.EnumToken.RuleNodeType, exports.EnumToken.KeyFramesRuleNodeType]); + accept = new Set([exports.EnumToken.RuleNodeType, exports.EnumToken.KeyframesRuleNodeType]); get ordering() { return 3; } @@ -22805,7 +22805,7 @@ exports.EnumToken.AtRuleNodeType, exports.EnumToken.RuleNodeType, exports.EnumToken.AtRuleTokenType, - exports.EnumToken.KeyFramesRuleNodeType, + exports.EnumToken.KeyframesRuleNodeType, ]; // @ts-ignore const features = Object.values(allFeatures).sort((a, b) => a.ordering - b.ordering); @@ -22863,7 +22863,7 @@ continue; } if (rules.includes(replacement.typ) && !Array.isArray(replacement[TOKENS])) { - replacement[TOKENS] = parseString(replacement.typ == exports.EnumToken.RuleNodeType || replacement.typ === exports.EnumToken.KeyFramesRuleNodeType + replacement[TOKENS] = parseString(replacement.typ == exports.EnumToken.RuleNodeType || replacement.typ === exports.EnumToken.KeyframesRuleNodeType ? replacement.sel : replacement.nam); } @@ -23138,8 +23138,8 @@ continue; } } - else if (node.typ === exports.EnumToken.KeyFramesRuleNodeType) { - if (previous?.typ === exports.EnumToken.KeyFramesRuleNodeType && + else if (node.typ === exports.EnumToken.KeyframesRuleNodeType) { + if (previous?.typ === exports.EnumToken.KeyframesRuleNodeType && node.sel === previous.sel) { // do not merge keyframes // https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@keyframes#resolving_duplicates @@ -23427,7 +23427,7 @@ } if (shouldMerge) { if (((node.typ === exports.EnumToken.RuleNodeType || - node.typ === exports.EnumToken.KeyFramesRuleNodeType) && + node.typ === exports.EnumToken.KeyframesRuleNodeType) && node.sel === previous.sel) || (node.typ == exports.EnumToken.AtRuleNodeType && node.nam !== "font-face" && @@ -23440,7 +23440,7 @@ continue; } else if (node.typ == previous?.typ && - [exports.EnumToken.KeyFramesRuleNodeType, exports.EnumToken.RuleNodeType].includes(node.typ)) { + [exports.EnumToken.KeyframesRuleNodeType, exports.EnumToken.RuleNodeType].includes(node.typ)) { const intersect = diff$1(previous, node, options); if (intersect != null) { if (intersect.node1.chi.length == 0) { @@ -24800,7 +24800,7 @@ [ exports.EnumToken.RuleNodeType, exports.EnumToken.AtRuleNodeType, - exports.EnumToken.KeyFramesRuleNodeType, + exports.EnumToken.KeyframesRuleNodeType, exports.EnumToken.KeyframesAtRuleNodeType, ].includes(node.typ)) { const source = options.sourcesMap.get(node[LOC].srcId); @@ -24938,7 +24938,7 @@ return children; case exports.EnumToken.AtRuleNodeType: case exports.EnumToken.RuleNodeType: - case exports.EnumToken.KeyFramesRuleNodeType: + case exports.EnumToken.KeyframesRuleNodeType: case exports.EnumToken.KeyframesAtRuleNodeType: if ([exports.EnumToken.AtRuleNodeType, exports.EnumToken.KeyframesAtRuleNodeType].includes(data.typ) && !("chi" in data)) { return `${indent}@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val};`; @@ -26019,7 +26019,7 @@ return acc; }, [])); return { - typ: exports.EnumToken.KeyFramesRuleNodeType, + typ: exports.EnumToken.KeyframesRuleNodeType, sel: [ ...splitTokenList(trimArray(tokens)).reduce((acc, curr) => { acc.add(curr.reduce((acc, curr) => acc + renderValue(curr, { minify: false }), "")); @@ -28711,6 +28711,99 @@ // if leading char is digit, prefix underscore (very rare) return (/^[0-9]/.test(result) ? "_" : "") + result; }); + function parseVisitors(options, valuesHandlers, preValuesHandlers, postValuesHandlers, errors, visitorsHandlersMap, preVisitorsHandlersMap, postVisitorsHandlersMap) { + const visitors = Object.entries(options.visitor); + let key; + let value; + let i; + for (i = 0; i < visitors.length; i++) { + key = visitors[i][0]; + value = visitors[i][1]; + if (Number.isInteger(+key)) { + if (Array.isArray(value)) { + visitors.splice(i + 1, 0, ...Object.entries(value)); + continue; + } + if (typeof value == "function") { + key = value.name; + } + } + if (Array.isArray(value)) { + // @ts-ignore + visitors.splice(i + 1, 0, ...value.map((item) => [key, item])); + continue; + } + if (key in exports.EnumToken) { + if (typeof value == "function") { + if (!valuesHandlers.has(exports.EnumToken[key])) { + valuesHandlers.set(exports.EnumToken[key], []); + } + valuesHandlers.get(exports.EnumToken[key]).push(value); + } + else if (typeof value == "object" && "type" in value && "handler" in value && value.type in exports.WalkerEvent) { + if (value.type == exports.WalkerEvent.Enter) { + if (!preValuesHandlers.has(exports.EnumToken[key])) { + preValuesHandlers.set(exports.EnumToken[key], []); + } + preValuesHandlers.get(exports.EnumToken[key]).push(value.handler); + } + else if (value.type == exports.WalkerEvent.Leave) { + if (!postValuesHandlers.has(exports.EnumToken[key])) { + postValuesHandlers.set(exports.EnumToken[key], []); + } + postValuesHandlers.get(exports.EnumToken[key]).push(value.handler); + } + } + else { + errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); + } + } + else if (["Declaration", "Rule", "AtRule", "KeyframesRule", "KeyframesAtRule"].includes(key)) { + if (typeof value == "function") { + if (!visitorsHandlersMap.has(key)) { + visitorsHandlersMap.set(key, []); + } + visitorsHandlersMap + .get(key) + .push(value); + } + else if (typeof value == "object") { + if ("type" in value && "handler" in value && value.type in exports.WalkerEvent) { + if (value.type == exports.WalkerEvent.Enter) { + if (!preVisitorsHandlersMap.has(key)) { + preVisitorsHandlersMap.set(key, []); + } + preVisitorsHandlersMap + .get(key) + .push(value.handler); + } + else if (value.type == exports.WalkerEvent.Leave) { + if (!postVisitorsHandlersMap.has(key)) { + postVisitorsHandlersMap.set(key, []); + } + postVisitorsHandlersMap + .get(key) + .push(value.handler); + } + } + else { + if (!visitorsHandlersMap.has(key)) { + visitorsHandlersMap.set(key, []); + } + visitorsHandlersMap + .get(key) + .push(value); + } + } + else { + errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); + } + } + else { + errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); + } + } + } /** * Parse css string * @param iter @@ -28793,114 +28886,13 @@ // @ts-ignore ignore error let parensMatch = 0; let curlyBracketMatch = 0; - if (options.visitor != null) { - valuesHandlers = new Map(); - preValuesHandlers = new Map(); - postValuesHandlers = new Map(); - preVisitorsHandlersMap = new Map(); - visitorsHandlersMap = new Map(); - postVisitorsHandlersMap = new Map(); - const visitors = Object.entries(options.visitor); - let key; - let value; - let i; - for (i = 0; i < visitors.length; i++) { - key = visitors[i][0]; - value = visitors[i][1]; - if (Number.isInteger(+key)) { - visitors.splice(i + 1, 0, ...Object.entries(value)); - continue; - } - if (Array.isArray(value)) { - // @ts-ignore - visitors.splice(i + 1, 0, ...value.map((item) => [key, item])); - continue; - } - if (key in exports.EnumToken) { - if (typeof value == "function") { - if (!valuesHandlers.has(exports.EnumToken[key])) { - valuesHandlers.set(exports.EnumToken[key], []); - } - valuesHandlers.get(exports.EnumToken[key]).push(value); - } - else if (typeof value == "object" && - "type" in value && - "handler" in value && - value.type in exports.WalkerEvent) { - if (value.type == exports.WalkerEvent.Enter) { - if (!preValuesHandlers.has(exports.EnumToken[key])) { - preValuesHandlers.set(exports.EnumToken[key], []); - } - preValuesHandlers - .get(exports.EnumToken[key]) - .push(value.handler); - } - else if (value.type == exports.WalkerEvent.Leave) { - if (!postValuesHandlers.has(exports.EnumToken[key])) { - postValuesHandlers.set(exports.EnumToken[key], []); - } - postValuesHandlers - .get(exports.EnumToken[key]) - .push(value.handler); - } - } - else { - errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); - } - } - else if (["Declaration", "Rule", "AtRule", "KeyframesRule", "KeyframesAtRule"].includes(key)) { - if (typeof value == "function") { - if (!visitorsHandlersMap.has(key)) { - visitorsHandlersMap.set(key, []); - } - visitorsHandlersMap - .get(key) - .push(value); - } - else if (typeof value == "object") { - if ("type" in value && "handler" in value && value.type in exports.WalkerEvent) { - if (value.type == exports.WalkerEvent.Enter) { - if (!preVisitorsHandlersMap.has(key)) { - preVisitorsHandlersMap.set(key, []); - } - preVisitorsHandlersMap - .get(key) - .push(value.handler); - } - else if (value.type == exports.WalkerEvent.Leave) { - if (!postVisitorsHandlersMap.has(key)) { - postVisitorsHandlersMap.set(key, []); - } - postVisitorsHandlersMap - .get(key) - .push(value.handler); - } - } - else { - if (!visitorsHandlersMap.has(key)) { - visitorsHandlersMap.set(key, []); - } - visitorsHandlersMap - .get(key) - .push(value); - } - } - else { - errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); - } - } - else { - errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); - } - } - } - if (Array.isArray(iter)) { - // @ts-expect-error - iter = iter[Symbol.iterator](); - } - while ( - // @ts-expect-error - (item = iter.next().value)) { + let currentItemIndex; + // if (Array.isArray(iter)) { + // // @ts-expect-error + // iter = iter[Symbol.iterator]() as Iterator; + // } + for (currentItemIndex = 0; currentItemIndex < iter.length; currentItemIndex++) { + item = iter[currentItemIndex]; stats.bytesIn = item.bytesIn; stats.tokensCount++; if (BadTokensTypes.includes(item.token.typ)) { @@ -28928,8 +28920,6 @@ curlyBracketMatch--; } tokens.push(item.token); - // console.debug([item.token, {parensMatch, curlyBracketMatch}]); - // if (parensMatch === 0) { if (parensMatch === 0 && (item.token.typ === exports.EnumToken.SemiColonTokenType || item.token.typ === exports.EnumToken.BlockStartTokenType || @@ -28945,8 +28935,7 @@ let inBlock = 1; tokens = [item.token]; do { - // @ts-expect-error - item = iter.next().value; + item = iter[++currentItemIndex]; if (item == null) { break; } @@ -29001,198 +28990,186 @@ ast = expand(ast); } let replacement; - let callable; if (options.visitor != null) { + valuesHandlers = new Map(); + preValuesHandlers = new Map(); + postValuesHandlers = new Map(); + preVisitorsHandlersMap = new Map(); + visitorsHandlersMap = new Map(); + postVisitorsHandlersMap = new Map(); + parseVisitors(options, valuesHandlers, preValuesHandlers, postValuesHandlers, errors, visitorsHandlersMap, preVisitorsHandlersMap, postVisitorsHandlersMap); let parens; - for (const result of walk(ast)) { + let genericKey; + const handlers = []; + const allHandlers = []; + if (preVisitorsHandlersMap.size > 0) { + allHandlers.push(preVisitorsHandlersMap); + } + if (preValuesHandlers.size > 0) { + allHandlers.push(preValuesHandlers); + } + if (visitorsHandlersMap.size > 0) { + allHandlers.push(visitorsHandlersMap); + } + if (valuesHandlers.size > 0) { + allHandlers.push(valuesHandlers); + } + if (postVisitorsHandlersMap.size > 0) { + allHandlers.push(postVisitorsHandlersMap); + } + if (postValuesHandlers.size > 0) { + allHandlers.push(postValuesHandlers); + } + let nodes = new Array(stats.tokensCount); + const subNodes = []; + let i; + let k; + let j; + let freeBlock = 1; + const includeTokens = preValuesHandlers.size > 0 || valuesHandlers.size > 0 || postValuesHandlers.size > 0; + nodes[0] = ast; + for (i = 0; i < nodes.length; i++) { + if (nodes[i] == null) { + break; + } + subNodes.length = 0; + if (includeTokens) { + switch (nodes[i].typ) { + case exports.EnumToken.RuleNodeType: + case exports.EnumToken.AtRuleNodeType: + case exports.EnumToken.KeyframesRuleNodeType: + case exports.EnumToken.KeyframesAtRuleNodeType: + subNodes.push(...nodes[i][TOKENS]); + break; + case exports.EnumToken.DeclarationNodeType: + subNodes.push(...nodes[i].val); + break; + } + } + if (nodes[i].chi != null) { + subNodes.push(...nodes[i].chi); + } + if (subNodes.length > 0) { + if (freeBlock <= i) { + freeBlock = i + 1; + } + for (k = 0; k < subNodes.length; k++) { + j = k + freeBlock; + nodes[j] = subNodes[k]; + nodes[j][PARENT] = nodes[i]; + } + freeBlock += subNodes.length; + } parens = null; - if (valuesHandlers.size > 0 || - preVisitorsHandlersMap.size > 0 || - visitorsHandlersMap.size > 0 || - postVisitorsHandlersMap.size > 0) { - if ((result.node.typ == exports.EnumToken.DeclarationNodeType && - (preVisitorsHandlersMap.has("Declaration") || - visitorsHandlersMap.has("Declaration") || - postVisitorsHandlersMap.has("Declaration"))) || - (result.node.typ == exports.EnumToken.AtRuleNodeType && - (preVisitorsHandlersMap.has("AtRule") || - visitorsHandlersMap.has("AtRule") || - postVisitorsHandlersMap.has("AtRule"))) || - (result.node.typ == exports.EnumToken.KeyframesAtRuleNodeType && - (preVisitorsHandlersMap.has("KeyframesAtRule") || - visitorsHandlersMap.has("KeyframesAtRule") || - postVisitorsHandlersMap.has("KeyframesAtRule")))) { - const handlers = []; - const key = result.node.typ == exports.EnumToken.DeclarationNodeType - ? "Declaration" - : result.node.typ == exports.EnumToken.AtRuleNodeType - ? "AtRule" - : "KeyframesAtRule"; - if (preVisitorsHandlersMap.has(key)) { - handlers.push( - // @ts-expect-error - ...preVisitorsHandlersMap.get(key)); - } - if (visitorsHandlersMap.has(key)) { - // @ts-ignore - handlers.push(...visitorsHandlersMap.get(key)); - } - if (postVisitorsHandlersMap.has(key)) { - // @ts-ignore - handlers.push(...postVisitorsHandlersMap.get(key)); - } - let node = result.node; - for (const handler of handlers) { - callable = - typeof handler == "function" - ? handler - : handler[camelize(node.typ === exports.EnumToken.DeclarationNodeType || - node.typ === exports.EnumToken.AtRuleNodeType - ? node.nam - : node.val)]; - if (callable == null) { - continue; + handlers.length = 0; + genericKey = + nodes[i].typ == exports.EnumToken.DeclarationNodeType + ? "Declaration" + : nodes[i].typ == exports.EnumToken.AtRuleNodeType + ? "AtRule" + : nodes[i].typ == exports.EnumToken.KeyframesAtRuleNodeType + ? "KeyframesAtRule" + : nodes[i].typ === exports.EnumToken.KeyframesRuleNodeType + ? "KeyframesRule" + : nodes[i].typ == exports.EnumToken.RuleNodeType + ? "Rule" + : nodes[i].typ == exports.EnumToken.KeyframesRuleNodeType + ? "KeyframesRule" + : null; + let keyName = nodes[i].typ == exports.EnumToken.DeclarationNodeType || nodes[i].typ == exports.EnumToken.AtRuleNodeType + ? camelize(nodes[i].nam) + : nodes[i].typ == exports.EnumToken.KeyframesAtRuleNodeType + ? camelize(nodes[i].val) + : null; + for (const map of allHandlers) { + // @ts-ignore + if (genericKey != null && map.has(genericKey)) { + // @ts-ignore + for (const handler of map.get(genericKey)) { + if (typeof handler == "function") { + handlers.push(handler); } - // @ts-expect-error - replacement = callable(node, result[PARENT], ast, function* () { - if (parens == null) { - // @ts-expect-error - parens = [...result.parents()]; + else if (Array.isArray(handler)) { + for (const h of handler) { + if (typeof h == "function") { + handlers.push(h); + } + // @ts-ignore + else if (h[keyName] != null) { + // @ts-ignore + handlers.push(h[keyName]); + } } - yield* parens[Symbol.iterator](); - }); - if (replacement == null) { - continue; } - if (replacement == node) { - continue; + else if (typeof handler.handler == "function") { + handlers.push(handler.handler); } // @ts-ignore - node = replacement; - if (Array.isArray(node)) { - break; + else if (typeof handler[keyName] == "function") { + // @ts-ignore + handlers.push(handler[keyName]); } } - if (node != result.node) { - replaceNodeOrValue(result.parent, result.node, node); - } } - else if ((result.node.typ == exports.EnumToken.RuleNodeType && - (preVisitorsHandlersMap.has("Rule") || - visitorsHandlersMap.has("Rule") || - postVisitorsHandlersMap.has("Rule"))) || - (result.node.typ == exports.EnumToken.KeyFramesRuleNodeType && - (preVisitorsHandlersMap.has("KeyframesRule") || - visitorsHandlersMap.has("KeyframesRule") || - postVisitorsHandlersMap.has("KeyframesRule")))) { - const handlers = []; - const key = result.node.typ == exports.EnumToken.RuleNodeType ? "Rule" : "KeyframesRule"; - if (preVisitorsHandlersMap.has(key)) { - handlers.push(...preVisitorsHandlersMap.get(key)); - } - if (visitorsHandlersMap.has(key)) { - handlers.push(...visitorsHandlersMap.get(key)); - } - if (postVisitorsHandlersMap.has(key)) { - handlers.push(...postVisitorsHandlersMap.get(key)); - } - let node = result.node; - for (const callable of handlers) { - replacement = callable(node, result.parent, result.root, - // @ts-expect-error - function* () { - if (parens == null) { - // @ts-expect-error - parens = [...result.parents()]; + // @ts-ignore + if (map.has(nodes[i].typ)) { + // @ts-ignore + for (const handler of map.get(nodes[i].typ)) { + if (typeof handler == "function") { + handlers.push(handler); + } + else if (Array.isArray(handler)) { + for (const h of handler) { + if (typeof h == "function") { + handlers.push(h); + } } - yield* parens[Symbol.iterator](); - }); - if (replacement == null) { - continue; } - if (replacement == node) { - continue; + else if (typeof handler.handler == "function") { + handlers.push(handler.handler); } // @ts-ignore - node = replacement; - // - if (Array.isArray(node)) { - break; + else if (typeof handler[keyName] == "function") { + // @ts-ignore + handlers.push(handler[keyName]); } } - // @ts-ignore - if (node != result.node) { - // @ts-ignore - replaceNodeOrValue(result.parent, result.node, node); - } } - else if (valuesHandlers.size > 0) { - let node = null; - node = result.node; - if (valuesHandlers.has(node.typ)) { - for (const valueHandler of valuesHandlers.get(node.typ)) { - callable = valueHandler; - replacement = callable(node, result.parent, ast, - // @ts-expect-error - function* () { - if (parens == null) { - // @ts-expect-error - parens = [...result.parents()]; - } - yield* parens[Symbol.iterator](); - }); - if (replacement == null) { - continue; - } - if (replacement != node) { - node = replacement; - } - } - } - if (node != result.node) { - // @ts-ignore - replaceNodeOrValue(result[PARENT], value, node); - } - const tokens = Array.isArray(result.node[TOKENS]) ? result.node[TOKENS] : []; - if (Array.isArray(result.node.val)) { - tokens.push(...result.node.val); - } - if (tokens.length == 0) { - continue; - } - for (const { value, parent, root, parents } of walkValues(tokens, result.node)) { - node = value; - if (valuesHandlers.has(node.typ)) { - let parens = null; - for (const valueHandler of valuesHandlers.get(node.typ)) { - callable = valueHandler; - // @ts-expect-error - let result = callable(node, parent, root, function* () { - if (parens == null) { - // @ts-expect-error - parens = [...parents()]; - } - yield* parens[Symbol.iterator](); - }); - if (result == null) { - continue; - } - if (result != node) { - node = result; - } - if (Array.isArray(node)) { - break; - } - } - } - if (node != value) { - // @ts-ignore - replaceNodeOrValue(parent, value, node); + } + if (handlers.length == 0) { + continue; + } + let node = nodes[i]; + for (const callable of handlers) { + replacement = callable(node, nodes[i][PARENT], ast, + // @ts-expect-error + function* () { + if (parens == null) { + let node = nodes[i][PARENT]; + while (node != null) { + yield node; + node = node[PARENT]; } } + }); + if (replacement == null) { + continue; + } + if (replacement == node) { + continue; } + // @ts-ignore + node = replacement; + // + if (Array.isArray(node)) { + break; + } + } + if (node != nodes[i]) { + replaceNodeOrValue(nodes[i][PARENT], nodes[i], node); } } + nodes = null; } if (invalidNodes.length > 0) { let count = invalidNodes.length; @@ -29256,7 +29233,7 @@ scoped: exports.ModuleScopeEnumOptions.Local, naming: exports.ModuleCaseTransformEnum.IgnoreCase, pattern: "", - generateScopedName, + generateScopedName: generateSyncScopedName, ...(typeof options.module != "object" ? {} : options.module), }; const parseModuleTime = performance.now(); @@ -29819,107 +29796,6 @@ let isAsync = typeof iter[Symbol.asyncIterator] === "function"; let parensMatch = 0; let curlyBracketMatch = 0; - if (options.visitor != null) { - valuesHandlers = new Map(); - preValuesHandlers = new Map(); - postValuesHandlers = new Map(); - preVisitorsHandlersMap = new Map(); - visitorsHandlersMap = new Map(); - postVisitorsHandlersMap = new Map(); - const visitors = Object.entries(options.visitor); - let key; - let value; - let i; - for (i = 0; i < visitors.length; i++) { - key = visitors[i][0]; - value = visitors[i][1]; - if (Number.isInteger(+key)) { - visitors.splice(i + 1, 0, ...Object.entries(value)); - continue; - } - if (Array.isArray(value)) { - // @ts-ignore - visitors.splice(i + 1, 0, ...value.map((item) => [key, item])); - continue; - } - if (key in exports.EnumToken) { - if (typeof value == "function") { - if (!valuesHandlers.has(exports.EnumToken[key])) { - valuesHandlers.set(exports.EnumToken[key], []); - } - valuesHandlers.get(exports.EnumToken[key]).push(value); - } - else if (typeof value == "object" && - "type" in value && - "handler" in value && - value.type in exports.WalkerEvent) { - if (value.type == exports.WalkerEvent.Enter) { - if (!preValuesHandlers.has(exports.EnumToken[key])) { - preValuesHandlers.set(exports.EnumToken[key], []); - } - preValuesHandlers - .get(exports.EnumToken[key]) - .push(value.handler); - } - else if (value.type == exports.WalkerEvent.Leave) { - if (!postValuesHandlers.has(exports.EnumToken[key])) { - postValuesHandlers.set(exports.EnumToken[key], []); - } - postValuesHandlers - .get(exports.EnumToken[key]) - .push(value.handler); - } - } - else { - errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); - } - } - else if (["Declaration", "Rule", "AtRule", "KeyframesRule", "KeyframesAtRule"].includes(key)) { - if (typeof value == "function") { - if (!visitorsHandlersMap.has(key)) { - visitorsHandlersMap.set(key, []); - } - visitorsHandlersMap - .get(key) - .push(value); - } - else if (typeof value == "object") { - if ("type" in value && "handler" in value && value.type in exports.WalkerEvent) { - if (value.type == exports.WalkerEvent.Enter) { - if (!preVisitorsHandlersMap.has(key)) { - preVisitorsHandlersMap.set(key, []); - } - preVisitorsHandlersMap - .get(key) - .push(value.handler); - } - else if (value.type == exports.WalkerEvent.Leave) { - if (!postVisitorsHandlersMap.has(key)) { - postVisitorsHandlersMap.set(key, []); - } - postVisitorsHandlersMap - .get(key) - .push(value.handler); - } - } - else { - if (!visitorsHandlersMap.has(key)) { - visitorsHandlersMap.set(key, []); - } - visitorsHandlersMap - .get(key) - .push(value); - } - } - else { - errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); - } - } - else { - errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); - } - } - } if (Array.isArray(iter)) { // @ts-expect-error iter = iter[Symbol.iterator](); @@ -30081,7 +29957,6 @@ ast = expand(ast); } let replacement; - let callable; while (stack.length > 0 && context != ast) { const previousNode = stack.pop(); context = (stack[stack.length - 1] ?? ast); @@ -30097,208 +29972,188 @@ break; } if (options.visitor != null) { + valuesHandlers = new Map(); + preValuesHandlers = new Map(); + postValuesHandlers = new Map(); + preVisitorsHandlersMap = new Map(); + visitorsHandlersMap = new Map(); + postVisitorsHandlersMap = new Map(); + parseVisitors(options, valuesHandlers, preValuesHandlers, postValuesHandlers, errors, visitorsHandlersMap, preVisitorsHandlersMap, postVisitorsHandlersMap); let parens; - for (const result of walk(ast)) { + let genericKey; + const handlers = []; + const allHandlers = []; + if (preVisitorsHandlersMap.size > 0) { + allHandlers.push(preVisitorsHandlersMap); + } + if (preValuesHandlers.size > 0) { + allHandlers.push(preValuesHandlers); + } + if (visitorsHandlersMap.size > 0) { + allHandlers.push(visitorsHandlersMap); + } + if (valuesHandlers.size > 0) { + allHandlers.push(valuesHandlers); + } + if (postVisitorsHandlersMap.size > 0) { + allHandlers.push(postVisitorsHandlersMap); + } + if (postValuesHandlers.size > 0) { + allHandlers.push(postValuesHandlers); + } + let nodes = new Array(stats.tokensCount); + const subNodes = []; + let i; + let k; + let j; + let freeblock = 1; + const includeTokens = preValuesHandlers.size > 0 || valuesHandlers.size > 0 || postValuesHandlers.size > 0; + nodes[0] = ast; + for (i = 0; i < nodes.length; i++) { + if (nodes[i] == null) { + break; + } + subNodes.length = 0; + if (includeTokens) { + switch (nodes[i].typ) { + case exports.EnumToken.RuleNodeType: + case exports.EnumToken.AtRuleNodeType: + case exports.EnumToken.KeyframesRuleNodeType: + case exports.EnumToken.KeyframesAtRuleNodeType: + subNodes.push(...nodes[i][TOKENS]); + break; + case exports.EnumToken.DeclarationNodeType: + subNodes.push(...nodes[i].val); + break; + } + } + if (nodes[i].chi != null) { + subNodes.push(...nodes[i].chi); + } + if (subNodes.length > 0) { + if (freeblock <= i) { + freeblock = i + 1; + } + for (k = 0; k < subNodes.length; k++) { + j = k + freeblock; + nodes[j] = subNodes[k]; + nodes[j][PARENT] = nodes[i]; + } + freeblock += subNodes.length; + } parens = null; - if (valuesHandlers.size > 0 || - preVisitorsHandlersMap.size > 0 || - visitorsHandlersMap.size > 0 || - postVisitorsHandlersMap.size > 0) { - if ((result.node.typ == exports.EnumToken.DeclarationNodeType && - (preVisitorsHandlersMap.has("Declaration") || - visitorsHandlersMap.has("Declaration") || - postVisitorsHandlersMap.has("Declaration"))) || - (result.node.typ == exports.EnumToken.AtRuleNodeType && - (preVisitorsHandlersMap.has("AtRule") || - visitorsHandlersMap.has("AtRule") || - postVisitorsHandlersMap.has("AtRule"))) || - (result.node.typ == exports.EnumToken.KeyframesAtRuleNodeType && - (preVisitorsHandlersMap.has("KeyframesAtRule") || - visitorsHandlersMap.has("KeyframesAtRule") || - postVisitorsHandlersMap.has("KeyframesAtRule")))) { - const handlers = []; - const key = result.node.typ == exports.EnumToken.DeclarationNodeType - ? "Declaration" - : result.node.typ == exports.EnumToken.AtRuleNodeType - ? "AtRule" - : "KeyframesAtRule"; - if (preVisitorsHandlersMap.has(key)) { - handlers.push( - // @ts-expect-error - ...preVisitorsHandlersMap.get(key)); - } - if (visitorsHandlersMap.has(key)) { - // @ts-ignore - handlers.push(...visitorsHandlersMap.get(key)); - } - if (postVisitorsHandlersMap.has(key)) { - // @ts-ignore - handlers.push(...postVisitorsHandlersMap.get(key)); - } - let node = result.node; - for (const handler of handlers) { - callable = - typeof handler == "function" - ? handler - : handler[camelize(node.typ === exports.EnumToken.DeclarationNodeType || - node.typ === exports.EnumToken.AtRuleNodeType - ? node.nam - : node.val)]; - if (callable == null) { - continue; + handlers.length = 0; + genericKey = + nodes[i].typ == exports.EnumToken.DeclarationNodeType + ? "Declaration" + : nodes[i].typ == exports.EnumToken.AtRuleNodeType + ? "AtRule" + : nodes[i].typ == exports.EnumToken.KeyframesAtRuleNodeType + ? "KeyframesAtRule" + : nodes[i].typ === exports.EnumToken.KeyframesRuleNodeType + ? "KeyframesRule" + : nodes[i].typ == exports.EnumToken.RuleNodeType + ? "Rule" + : nodes[i].typ == exports.EnumToken.KeyframesRuleNodeType + ? "KeyframesRule" + : null; + let keyName = nodes[i].typ == exports.EnumToken.DeclarationNodeType || nodes[i].typ == exports.EnumToken.AtRuleNodeType + ? camelize(nodes[i].nam) + : nodes[i].typ == exports.EnumToken.KeyframesAtRuleNodeType + ? camelize(nodes[i].val) + : null; + for (const map of allHandlers) { + // @ts-ignore + if (genericKey != null && map.has(genericKey)) { + // @ts-ignore + for (const handler of map.get(genericKey)) { + if (typeof handler == "function") { + handlers.push(handler); } - // @ts-expect-error - replacement = callable(node, result[PARENT], ast, function* () { - if (parens == null) { - // @ts-expect-error - parens = [...result.parents()]; + else if (Array.isArray(handler)) { + for (const h of handler) { + if (typeof h == "function") { + handlers.push(h); + } + // @ts-ignore + else if (h[keyName] != null) { + // @ts-ignore + handlers.push(h[keyName]); + } } - yield* parens[Symbol.iterator](); - }); - if (replacement == null) { - continue; - } - if (replacement instanceof Promise) { - replacement = await replacement; } - if (replacement == null || replacement == node) { - continue; + else if (typeof handler.handler == "function") { + handlers.push(handler.handler); } // @ts-ignore - node = replacement; - if (Array.isArray(node)) { - break; + else if (typeof handler[keyName] == "function") { + // @ts-ignore + handlers.push(handler[keyName]); } } - if (node != result.node) { - replaceNodeOrValue(result.parent, result.node, node); - } } - else if ((result.node.typ == exports.EnumToken.RuleNodeType && - (preVisitorsHandlersMap.has("Rule") || - visitorsHandlersMap.has("Rule") || - postVisitorsHandlersMap.has("Rule"))) || - (result.node.typ == exports.EnumToken.KeyFramesRuleNodeType && - (preVisitorsHandlersMap.has("KeyframesRule") || - visitorsHandlersMap.has("KeyframesRule") || - postVisitorsHandlersMap.has("KeyframesRule")))) { - const handlers = []; - const key = result.node.typ == exports.EnumToken.RuleNodeType ? "Rule" : "KeyframesRule"; - if (preVisitorsHandlersMap.has(key)) { - handlers.push(...preVisitorsHandlersMap.get(key)); - } - if (visitorsHandlersMap.has(key)) { - handlers.push(...visitorsHandlersMap.get(key)); - } - if (postVisitorsHandlersMap.has(key)) { - handlers.push(...postVisitorsHandlersMap.get(key)); - } - let node = result.node; - for (const callable of handlers) { - replacement = callable(node, result.parent, result.root, - // @ts-expect-error - function* () { - if (parens == null) { - // @ts-expect-error - parens = [...result.parents()]; - } - yield* parens[Symbol.iterator](); - }); - if (replacement == null) { - continue; + // @ts-ignore + if (map.has(nodes[i].typ)) { + // @ts-ignore + for (const handler of map.get(nodes[i].typ)) { + if (typeof handler == "function") { + handlers.push(handler); } - if (replacement instanceof Promise) { - replacement = await replacement; + else if (Array.isArray(handler)) { + for (const h of handler) { + if (typeof h == "function") { + handlers.push(h); + } + } } - if (replacement == null || replacement == node) { - continue; + else if (typeof handler.handler == "function") { + handlers.push(handler.handler); } // @ts-ignore - node = replacement; - // - if (Array.isArray(node)) { - break; + else if (typeof handler[keyName] == "function") { + // @ts-ignore + handlers.push(handler[keyName]); } } - // @ts-ignore - if (node != result.node) { - // @ts-ignore - replaceNodeOrValue(result.parent, result.node, node); - } } - else if (valuesHandlers.size > 0) { - let node = null; - node = result.node; - if (valuesHandlers.has(node.typ)) { - for (const valueHandler of valuesHandlers.get(node.typ)) { - callable = valueHandler; - replacement = callable(node, result.parent, ast, - // @ts-expect-error - function* () { - if (parens == null) { - // @ts-expect-error - parens = [...result.parents()]; - } - yield* parens[Symbol.iterator](); - }); - if (replacement == null) { - continue; - } - if (replacement instanceof Promise) { - replacement = await replacement; - } - if (replacement != null && replacement != node) { - node = replacement; - } - } - } - if (node != result.node) { - // @ts-ignore - replaceNodeOrValue(result[PARENT], value, node); - } - const tokens = Array.isArray(result.node[TOKENS]) ? result.node[TOKENS] : []; - if (Array.isArray(result.node.val)) { - tokens.push(...result.node.val); - } - if (tokens.length == 0) { - continue; - } - for (const { value, parent, root, parents } of walkValues(tokens, result.node)) { - node = value; - if (valuesHandlers.has(node.typ)) { - let parens = null; - for (const valueHandler of valuesHandlers.get(node.typ)) { - callable = valueHandler; - // @ts-expect-error - let result = callable(node, parent, root, function* () { - if (parens == null) { - // @ts-expect-error - parens = [...parents()]; - } - yield* parens[Symbol.iterator](); - }); - if (result == null) { - continue; - } - if (result instanceof Promise) { - result = await result; - } - if (result != null && result != node) { - node = result; - } - if (Array.isArray(node)) { - break; - } - } - } - if (node != value) { - // @ts-ignore - replaceNodeOrValue(parent, value, node); + } + if (handlers.length == 0) { + continue; + } + let node = nodes[i]; + for (const callable of handlers) { + replacement = callable(node, nodes[i][PARENT], ast, + // @ts-expect-error + function* () { + if (parens == null) { + let node = nodes[i][PARENT]; + while (node != null) { + yield node; + node = node[PARENT]; } } + }); + if (replacement == null) { + continue; + } + if (replacement instanceof Promise) { + replacement = await replacement; + } + if (replacement == null || replacement == node) { + continue; + } + // @ts-ignore + node = replacement; + // + if (Array.isArray(node)) { + break; } } + if (node != nodes[i]) { + replaceNodeOrValue(nodes[i][PARENT], nodes[i], node); + } } + nodes = null; } if (invalidNodes.length > 0) { let count = invalidNodes.length; diff --git a/dist/index.cjs b/dist/index.cjs index 481a526d..f62fb5f0 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -394,7 +394,7 @@ exports.EnumToken = void 0; /** * keyframe rule node type */ - EnumToken[EnumToken["KeyFramesRuleNodeType"] = 73] = "KeyFramesRuleNodeType"; + EnumToken[EnumToken["KeyframesRuleNodeType"] = 73] = "KeyframesRuleNodeType"; /** * class selector token type */ @@ -19722,7 +19722,7 @@ class ComputeShorthandFeature { accept = new Set([ exports.EnumToken.RuleNodeType, exports.EnumToken.AtRuleNodeType, - exports.EnumToken.KeyFramesRuleNodeType, + exports.EnumToken.KeyframesRuleNodeType, ]); get ordering() { return 10; @@ -21063,7 +21063,7 @@ function splitTransformList(transformList) { } class TransformCssFeature { - accept = new Set([exports.EnumToken.RuleNodeType, exports.EnumToken.KeyFramesRuleNodeType]); + accept = new Set([exports.EnumToken.RuleNodeType, exports.EnumToken.KeyframesRuleNodeType]); get ordering() { return 3; } @@ -22808,7 +22808,7 @@ const rules = [ exports.EnumToken.AtRuleNodeType, exports.EnumToken.RuleNodeType, exports.EnumToken.AtRuleTokenType, - exports.EnumToken.KeyFramesRuleNodeType, + exports.EnumToken.KeyframesRuleNodeType, ]; // @ts-ignore const features = Object.values(allFeatures).sort((a, b) => a.ordering - b.ordering); @@ -22866,7 +22866,7 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent, co continue; } if (rules.includes(replacement.typ) && !Array.isArray(replacement[TOKENS])) { - replacement[TOKENS] = parseString(replacement.typ == exports.EnumToken.RuleNodeType || replacement.typ === exports.EnumToken.KeyFramesRuleNodeType + replacement[TOKENS] = parseString(replacement.typ == exports.EnumToken.RuleNodeType || replacement.typ === exports.EnumToken.KeyframesRuleNodeType ? replacement.sel : replacement.nam); } @@ -23141,8 +23141,8 @@ function doMinify(ast, options = {}, recursive = false, errors, nestingContent, continue; } } - else if (node.typ === exports.EnumToken.KeyFramesRuleNodeType) { - if (previous?.typ === exports.EnumToken.KeyFramesRuleNodeType && + else if (node.typ === exports.EnumToken.KeyframesRuleNodeType) { + if (previous?.typ === exports.EnumToken.KeyframesRuleNodeType && node.sel === previous.sel) { // do not merge keyframes // https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@keyframes#resolving_duplicates @@ -23430,7 +23430,7 @@ function doMinify(ast, options = {}, recursive = false, errors, nestingContent, } if (shouldMerge) { if (((node.typ === exports.EnumToken.RuleNodeType || - node.typ === exports.EnumToken.KeyFramesRuleNodeType) && + node.typ === exports.EnumToken.KeyframesRuleNodeType) && node.sel === previous.sel) || (node.typ == exports.EnumToken.AtRuleNodeType && node.nam !== "font-face" && @@ -23443,7 +23443,7 @@ function doMinify(ast, options = {}, recursive = false, errors, nestingContent, continue; } else if (node.typ == previous?.typ && - [exports.EnumToken.KeyFramesRuleNodeType, exports.EnumToken.RuleNodeType].includes(node.typ)) { + [exports.EnumToken.KeyframesRuleNodeType, exports.EnumToken.RuleNodeType].includes(node.typ)) { const intersect = diff$1(previous, node, options); if (intersect != null) { if (intersect.node1.chi.length == 0) { @@ -24803,7 +24803,7 @@ function updateSourceMap(node, options, cache, sourcemaps, sourceLocation, lines [ exports.EnumToken.RuleNodeType, exports.EnumToken.AtRuleNodeType, - exports.EnumToken.KeyFramesRuleNodeType, + exports.EnumToken.KeyframesRuleNodeType, exports.EnumToken.KeyframesAtRuleNodeType, ].includes(node.typ)) { const source = options.sourcesMap.get(node[LOC].srcId); @@ -24941,7 +24941,7 @@ function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, erro return children; case exports.EnumToken.AtRuleNodeType: case exports.EnumToken.RuleNodeType: - case exports.EnumToken.KeyFramesRuleNodeType: + case exports.EnumToken.KeyframesRuleNodeType: case exports.EnumToken.KeyframesAtRuleNodeType: if ([exports.EnumToken.AtRuleNodeType, exports.EnumToken.KeyframesAtRuleNodeType].includes(data.typ) && !("chi" in data)) { return `${indent}@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val};`; @@ -26022,7 +26022,7 @@ function parseSelector(tokens, context, options, errors) { return acc; }, [])); return { - typ: exports.EnumToken.KeyFramesRuleNodeType, + typ: exports.EnumToken.KeyframesRuleNodeType, sel: [ ...splitTokenList(trimArray(tokens)).reduce((acc, curr) => { acc.add(curr.reduce((acc, curr) => acc + renderValue(curr, { minify: false }), "")); @@ -28714,6 +28714,99 @@ const generateSyncScopedName = memoize((localName, filePath, pattern, hashLength // if leading char is digit, prefix underscore (very rare) return (/^[0-9]/.test(result) ? "_" : "") + result; }); +function parseVisitors(options, valuesHandlers, preValuesHandlers, postValuesHandlers, errors, visitorsHandlersMap, preVisitorsHandlersMap, postVisitorsHandlersMap) { + const visitors = Object.entries(options.visitor); + let key; + let value; + let i; + for (i = 0; i < visitors.length; i++) { + key = visitors[i][0]; + value = visitors[i][1]; + if (Number.isInteger(+key)) { + if (Array.isArray(value)) { + visitors.splice(i + 1, 0, ...Object.entries(value)); + continue; + } + if (typeof value == "function") { + key = value.name; + } + } + if (Array.isArray(value)) { + // @ts-ignore + visitors.splice(i + 1, 0, ...value.map((item) => [key, item])); + continue; + } + if (key in exports.EnumToken) { + if (typeof value == "function") { + if (!valuesHandlers.has(exports.EnumToken[key])) { + valuesHandlers.set(exports.EnumToken[key], []); + } + valuesHandlers.get(exports.EnumToken[key]).push(value); + } + else if (typeof value == "object" && "type" in value && "handler" in value && value.type in exports.WalkerEvent) { + if (value.type == exports.WalkerEvent.Enter) { + if (!preValuesHandlers.has(exports.EnumToken[key])) { + preValuesHandlers.set(exports.EnumToken[key], []); + } + preValuesHandlers.get(exports.EnumToken[key]).push(value.handler); + } + else if (value.type == exports.WalkerEvent.Leave) { + if (!postValuesHandlers.has(exports.EnumToken[key])) { + postValuesHandlers.set(exports.EnumToken[key], []); + } + postValuesHandlers.get(exports.EnumToken[key]).push(value.handler); + } + } + else { + errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); + } + } + else if (["Declaration", "Rule", "AtRule", "KeyframesRule", "KeyframesAtRule"].includes(key)) { + if (typeof value == "function") { + if (!visitorsHandlersMap.has(key)) { + visitorsHandlersMap.set(key, []); + } + visitorsHandlersMap + .get(key) + .push(value); + } + else if (typeof value == "object") { + if ("type" in value && "handler" in value && value.type in exports.WalkerEvent) { + if (value.type == exports.WalkerEvent.Enter) { + if (!preVisitorsHandlersMap.has(key)) { + preVisitorsHandlersMap.set(key, []); + } + preVisitorsHandlersMap + .get(key) + .push(value.handler); + } + else if (value.type == exports.WalkerEvent.Leave) { + if (!postVisitorsHandlersMap.has(key)) { + postVisitorsHandlersMap.set(key, []); + } + postVisitorsHandlersMap + .get(key) + .push(value.handler); + } + } + else { + if (!visitorsHandlersMap.has(key)) { + visitorsHandlersMap.set(key, []); + } + visitorsHandlersMap + .get(key) + .push(value); + } + } + else { + errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); + } + } + else { + errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); + } + } +} /** * Parse css string * @param iter @@ -28796,114 +28889,13 @@ function doParseSync(iter, options = {}) { // @ts-ignore ignore error let parensMatch = 0; let curlyBracketMatch = 0; - if (options.visitor != null) { - valuesHandlers = new Map(); - preValuesHandlers = new Map(); - postValuesHandlers = new Map(); - preVisitorsHandlersMap = new Map(); - visitorsHandlersMap = new Map(); - postVisitorsHandlersMap = new Map(); - const visitors = Object.entries(options.visitor); - let key; - let value; - let i; - for (i = 0; i < visitors.length; i++) { - key = visitors[i][0]; - value = visitors[i][1]; - if (Number.isInteger(+key)) { - visitors.splice(i + 1, 0, ...Object.entries(value)); - continue; - } - if (Array.isArray(value)) { - // @ts-ignore - visitors.splice(i + 1, 0, ...value.map((item) => [key, item])); - continue; - } - if (key in exports.EnumToken) { - if (typeof value == "function") { - if (!valuesHandlers.has(exports.EnumToken[key])) { - valuesHandlers.set(exports.EnumToken[key], []); - } - valuesHandlers.get(exports.EnumToken[key]).push(value); - } - else if (typeof value == "object" && - "type" in value && - "handler" in value && - value.type in exports.WalkerEvent) { - if (value.type == exports.WalkerEvent.Enter) { - if (!preValuesHandlers.has(exports.EnumToken[key])) { - preValuesHandlers.set(exports.EnumToken[key], []); - } - preValuesHandlers - .get(exports.EnumToken[key]) - .push(value.handler); - } - else if (value.type == exports.WalkerEvent.Leave) { - if (!postValuesHandlers.has(exports.EnumToken[key])) { - postValuesHandlers.set(exports.EnumToken[key], []); - } - postValuesHandlers - .get(exports.EnumToken[key]) - .push(value.handler); - } - } - else { - errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); - } - } - else if (["Declaration", "Rule", "AtRule", "KeyframesRule", "KeyframesAtRule"].includes(key)) { - if (typeof value == "function") { - if (!visitorsHandlersMap.has(key)) { - visitorsHandlersMap.set(key, []); - } - visitorsHandlersMap - .get(key) - .push(value); - } - else if (typeof value == "object") { - if ("type" in value && "handler" in value && value.type in exports.WalkerEvent) { - if (value.type == exports.WalkerEvent.Enter) { - if (!preVisitorsHandlersMap.has(key)) { - preVisitorsHandlersMap.set(key, []); - } - preVisitorsHandlersMap - .get(key) - .push(value.handler); - } - else if (value.type == exports.WalkerEvent.Leave) { - if (!postVisitorsHandlersMap.has(key)) { - postVisitorsHandlersMap.set(key, []); - } - postVisitorsHandlersMap - .get(key) - .push(value.handler); - } - } - else { - if (!visitorsHandlersMap.has(key)) { - visitorsHandlersMap.set(key, []); - } - visitorsHandlersMap - .get(key) - .push(value); - } - } - else { - errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); - } - } - else { - errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); - } - } - } - if (Array.isArray(iter)) { - // @ts-expect-error - iter = iter[Symbol.iterator](); - } - while ( - // @ts-expect-error - (item = iter.next().value)) { + let currentItemIndex; + // if (Array.isArray(iter)) { + // // @ts-expect-error + // iter = iter[Symbol.iterator]() as Iterator; + // } + for (currentItemIndex = 0; currentItemIndex < iter.length; currentItemIndex++) { + item = iter[currentItemIndex]; stats.bytesIn = item.bytesIn; stats.tokensCount++; if (BadTokensTypes.includes(item.token.typ)) { @@ -28931,8 +28923,6 @@ function doParseSync(iter, options = {}) { curlyBracketMatch--; } tokens.push(item.token); - // console.debug([item.token, {parensMatch, curlyBracketMatch}]); - // if (parensMatch === 0) { if (parensMatch === 0 && (item.token.typ === exports.EnumToken.SemiColonTokenType || item.token.typ === exports.EnumToken.BlockStartTokenType || @@ -28948,8 +28938,7 @@ function doParseSync(iter, options = {}) { let inBlock = 1; tokens = [item.token]; do { - // @ts-expect-error - item = iter.next().value; + item = iter[++currentItemIndex]; if (item == null) { break; } @@ -29004,198 +28993,186 @@ function doParseSync(iter, options = {}) { ast = expand(ast); } let replacement; - let callable; if (options.visitor != null) { + valuesHandlers = new Map(); + preValuesHandlers = new Map(); + postValuesHandlers = new Map(); + preVisitorsHandlersMap = new Map(); + visitorsHandlersMap = new Map(); + postVisitorsHandlersMap = new Map(); + parseVisitors(options, valuesHandlers, preValuesHandlers, postValuesHandlers, errors, visitorsHandlersMap, preVisitorsHandlersMap, postVisitorsHandlersMap); let parens; - for (const result of walk(ast)) { + let genericKey; + const handlers = []; + const allHandlers = []; + if (preVisitorsHandlersMap.size > 0) { + allHandlers.push(preVisitorsHandlersMap); + } + if (preValuesHandlers.size > 0) { + allHandlers.push(preValuesHandlers); + } + if (visitorsHandlersMap.size > 0) { + allHandlers.push(visitorsHandlersMap); + } + if (valuesHandlers.size > 0) { + allHandlers.push(valuesHandlers); + } + if (postVisitorsHandlersMap.size > 0) { + allHandlers.push(postVisitorsHandlersMap); + } + if (postValuesHandlers.size > 0) { + allHandlers.push(postValuesHandlers); + } + let nodes = new Array(stats.tokensCount); + const subNodes = []; + let i; + let k; + let j; + let freeBlock = 1; + const includeTokens = preValuesHandlers.size > 0 || valuesHandlers.size > 0 || postValuesHandlers.size > 0; + nodes[0] = ast; + for (i = 0; i < nodes.length; i++) { + if (nodes[i] == null) { + break; + } + subNodes.length = 0; + if (includeTokens) { + switch (nodes[i].typ) { + case exports.EnumToken.RuleNodeType: + case exports.EnumToken.AtRuleNodeType: + case exports.EnumToken.KeyframesRuleNodeType: + case exports.EnumToken.KeyframesAtRuleNodeType: + subNodes.push(...nodes[i][TOKENS]); + break; + case exports.EnumToken.DeclarationNodeType: + subNodes.push(...nodes[i].val); + break; + } + } + if (nodes[i].chi != null) { + subNodes.push(...nodes[i].chi); + } + if (subNodes.length > 0) { + if (freeBlock <= i) { + freeBlock = i + 1; + } + for (k = 0; k < subNodes.length; k++) { + j = k + freeBlock; + nodes[j] = subNodes[k]; + nodes[j][PARENT] = nodes[i]; + } + freeBlock += subNodes.length; + } parens = null; - if (valuesHandlers.size > 0 || - preVisitorsHandlersMap.size > 0 || - visitorsHandlersMap.size > 0 || - postVisitorsHandlersMap.size > 0) { - if ((result.node.typ == exports.EnumToken.DeclarationNodeType && - (preVisitorsHandlersMap.has("Declaration") || - visitorsHandlersMap.has("Declaration") || - postVisitorsHandlersMap.has("Declaration"))) || - (result.node.typ == exports.EnumToken.AtRuleNodeType && - (preVisitorsHandlersMap.has("AtRule") || - visitorsHandlersMap.has("AtRule") || - postVisitorsHandlersMap.has("AtRule"))) || - (result.node.typ == exports.EnumToken.KeyframesAtRuleNodeType && - (preVisitorsHandlersMap.has("KeyframesAtRule") || - visitorsHandlersMap.has("KeyframesAtRule") || - postVisitorsHandlersMap.has("KeyframesAtRule")))) { - const handlers = []; - const key = result.node.typ == exports.EnumToken.DeclarationNodeType - ? "Declaration" - : result.node.typ == exports.EnumToken.AtRuleNodeType - ? "AtRule" - : "KeyframesAtRule"; - if (preVisitorsHandlersMap.has(key)) { - handlers.push( - // @ts-expect-error - ...preVisitorsHandlersMap.get(key)); - } - if (visitorsHandlersMap.has(key)) { - // @ts-ignore - handlers.push(...visitorsHandlersMap.get(key)); - } - if (postVisitorsHandlersMap.has(key)) { - // @ts-ignore - handlers.push(...postVisitorsHandlersMap.get(key)); - } - let node = result.node; - for (const handler of handlers) { - callable = - typeof handler == "function" - ? handler - : handler[camelize(node.typ === exports.EnumToken.DeclarationNodeType || - node.typ === exports.EnumToken.AtRuleNodeType - ? node.nam - : node.val)]; - if (callable == null) { - continue; + handlers.length = 0; + genericKey = + nodes[i].typ == exports.EnumToken.DeclarationNodeType + ? "Declaration" + : nodes[i].typ == exports.EnumToken.AtRuleNodeType + ? "AtRule" + : nodes[i].typ == exports.EnumToken.KeyframesAtRuleNodeType + ? "KeyframesAtRule" + : nodes[i].typ === exports.EnumToken.KeyframesRuleNodeType + ? "KeyframesRule" + : nodes[i].typ == exports.EnumToken.RuleNodeType + ? "Rule" + : nodes[i].typ == exports.EnumToken.KeyframesRuleNodeType + ? "KeyframesRule" + : null; + let keyName = nodes[i].typ == exports.EnumToken.DeclarationNodeType || nodes[i].typ == exports.EnumToken.AtRuleNodeType + ? camelize(nodes[i].nam) + : nodes[i].typ == exports.EnumToken.KeyframesAtRuleNodeType + ? camelize(nodes[i].val) + : null; + for (const map of allHandlers) { + // @ts-ignore + if (genericKey != null && map.has(genericKey)) { + // @ts-ignore + for (const handler of map.get(genericKey)) { + if (typeof handler == "function") { + handlers.push(handler); } - // @ts-expect-error - replacement = callable(node, result[PARENT], ast, function* () { - if (parens == null) { - // @ts-expect-error - parens = [...result.parents()]; + else if (Array.isArray(handler)) { + for (const h of handler) { + if (typeof h == "function") { + handlers.push(h); + } + // @ts-ignore + else if (h[keyName] != null) { + // @ts-ignore + handlers.push(h[keyName]); + } } - yield* parens[Symbol.iterator](); - }); - if (replacement == null) { - continue; } - if (replacement == node) { - continue; + else if (typeof handler.handler == "function") { + handlers.push(handler.handler); } // @ts-ignore - node = replacement; - if (Array.isArray(node)) { - break; + else if (typeof handler[keyName] == "function") { + // @ts-ignore + handlers.push(handler[keyName]); } } - if (node != result.node) { - replaceNodeOrValue(result.parent, result.node, node); - } } - else if ((result.node.typ == exports.EnumToken.RuleNodeType && - (preVisitorsHandlersMap.has("Rule") || - visitorsHandlersMap.has("Rule") || - postVisitorsHandlersMap.has("Rule"))) || - (result.node.typ == exports.EnumToken.KeyFramesRuleNodeType && - (preVisitorsHandlersMap.has("KeyframesRule") || - visitorsHandlersMap.has("KeyframesRule") || - postVisitorsHandlersMap.has("KeyframesRule")))) { - const handlers = []; - const key = result.node.typ == exports.EnumToken.RuleNodeType ? "Rule" : "KeyframesRule"; - if (preVisitorsHandlersMap.has(key)) { - handlers.push(...preVisitorsHandlersMap.get(key)); - } - if (visitorsHandlersMap.has(key)) { - handlers.push(...visitorsHandlersMap.get(key)); - } - if (postVisitorsHandlersMap.has(key)) { - handlers.push(...postVisitorsHandlersMap.get(key)); - } - let node = result.node; - for (const callable of handlers) { - replacement = callable(node, result.parent, result.root, - // @ts-expect-error - function* () { - if (parens == null) { - // @ts-expect-error - parens = [...result.parents()]; + // @ts-ignore + if (map.has(nodes[i].typ)) { + // @ts-ignore + for (const handler of map.get(nodes[i].typ)) { + if (typeof handler == "function") { + handlers.push(handler); + } + else if (Array.isArray(handler)) { + for (const h of handler) { + if (typeof h == "function") { + handlers.push(h); + } } - yield* parens[Symbol.iterator](); - }); - if (replacement == null) { - continue; } - if (replacement == node) { - continue; + else if (typeof handler.handler == "function") { + handlers.push(handler.handler); } // @ts-ignore - node = replacement; - // - if (Array.isArray(node)) { - break; + else if (typeof handler[keyName] == "function") { + // @ts-ignore + handlers.push(handler[keyName]); } } - // @ts-ignore - if (node != result.node) { - // @ts-ignore - replaceNodeOrValue(result.parent, result.node, node); - } } - else if (valuesHandlers.size > 0) { - let node = null; - node = result.node; - if (valuesHandlers.has(node.typ)) { - for (const valueHandler of valuesHandlers.get(node.typ)) { - callable = valueHandler; - replacement = callable(node, result.parent, ast, - // @ts-expect-error - function* () { - if (parens == null) { - // @ts-expect-error - parens = [...result.parents()]; - } - yield* parens[Symbol.iterator](); - }); - if (replacement == null) { - continue; - } - if (replacement != node) { - node = replacement; - } - } - } - if (node != result.node) { - // @ts-ignore - replaceNodeOrValue(result[PARENT], value, node); - } - const tokens = Array.isArray(result.node[TOKENS]) ? result.node[TOKENS] : []; - if (Array.isArray(result.node.val)) { - tokens.push(...result.node.val); - } - if (tokens.length == 0) { - continue; - } - for (const { value, parent, root, parents } of walkValues(tokens, result.node)) { - node = value; - if (valuesHandlers.has(node.typ)) { - let parens = null; - for (const valueHandler of valuesHandlers.get(node.typ)) { - callable = valueHandler; - // @ts-expect-error - let result = callable(node, parent, root, function* () { - if (parens == null) { - // @ts-expect-error - parens = [...parents()]; - } - yield* parens[Symbol.iterator](); - }); - if (result == null) { - continue; - } - if (result != node) { - node = result; - } - if (Array.isArray(node)) { - break; - } - } - } - if (node != value) { - // @ts-ignore - replaceNodeOrValue(parent, value, node); + } + if (handlers.length == 0) { + continue; + } + let node = nodes[i]; + for (const callable of handlers) { + replacement = callable(node, nodes[i][PARENT], ast, + // @ts-expect-error + function* () { + if (parens == null) { + let node = nodes[i][PARENT]; + while (node != null) { + yield node; + node = node[PARENT]; } } + }); + if (replacement == null) { + continue; + } + if (replacement == node) { + continue; } + // @ts-ignore + node = replacement; + // + if (Array.isArray(node)) { + break; + } + } + if (node != nodes[i]) { + replaceNodeOrValue(nodes[i][PARENT], nodes[i], node); } } + nodes = null; } if (invalidNodes.length > 0) { let count = invalidNodes.length; @@ -29259,7 +29236,7 @@ function doParseSync(iter, options = {}) { scoped: exports.ModuleScopeEnumOptions.Local, naming: exports.ModuleCaseTransformEnum.IgnoreCase, pattern: "", - generateScopedName, + generateScopedName: generateSyncScopedName, ...(typeof options.module != "object" ? {} : options.module), }; const parseModuleTime = performance.now(); @@ -29822,107 +29799,6 @@ async function doParse(iter, options = {}) { let isAsync = typeof iter[Symbol.asyncIterator] === "function"; let parensMatch = 0; let curlyBracketMatch = 0; - if (options.visitor != null) { - valuesHandlers = new Map(); - preValuesHandlers = new Map(); - postValuesHandlers = new Map(); - preVisitorsHandlersMap = new Map(); - visitorsHandlersMap = new Map(); - postVisitorsHandlersMap = new Map(); - const visitors = Object.entries(options.visitor); - let key; - let value; - let i; - for (i = 0; i < visitors.length; i++) { - key = visitors[i][0]; - value = visitors[i][1]; - if (Number.isInteger(+key)) { - visitors.splice(i + 1, 0, ...Object.entries(value)); - continue; - } - if (Array.isArray(value)) { - // @ts-ignore - visitors.splice(i + 1, 0, ...value.map((item) => [key, item])); - continue; - } - if (key in exports.EnumToken) { - if (typeof value == "function") { - if (!valuesHandlers.has(exports.EnumToken[key])) { - valuesHandlers.set(exports.EnumToken[key], []); - } - valuesHandlers.get(exports.EnumToken[key]).push(value); - } - else if (typeof value == "object" && - "type" in value && - "handler" in value && - value.type in exports.WalkerEvent) { - if (value.type == exports.WalkerEvent.Enter) { - if (!preValuesHandlers.has(exports.EnumToken[key])) { - preValuesHandlers.set(exports.EnumToken[key], []); - } - preValuesHandlers - .get(exports.EnumToken[key]) - .push(value.handler); - } - else if (value.type == exports.WalkerEvent.Leave) { - if (!postValuesHandlers.has(exports.EnumToken[key])) { - postValuesHandlers.set(exports.EnumToken[key], []); - } - postValuesHandlers - .get(exports.EnumToken[key]) - .push(value.handler); - } - } - else { - errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); - } - } - else if (["Declaration", "Rule", "AtRule", "KeyframesRule", "KeyframesAtRule"].includes(key)) { - if (typeof value == "function") { - if (!visitorsHandlersMap.has(key)) { - visitorsHandlersMap.set(key, []); - } - visitorsHandlersMap - .get(key) - .push(value); - } - else if (typeof value == "object") { - if ("type" in value && "handler" in value && value.type in exports.WalkerEvent) { - if (value.type == exports.WalkerEvent.Enter) { - if (!preVisitorsHandlersMap.has(key)) { - preVisitorsHandlersMap.set(key, []); - } - preVisitorsHandlersMap - .get(key) - .push(value.handler); - } - else if (value.type == exports.WalkerEvent.Leave) { - if (!postVisitorsHandlersMap.has(key)) { - postVisitorsHandlersMap.set(key, []); - } - postVisitorsHandlersMap - .get(key) - .push(value.handler); - } - } - else { - if (!visitorsHandlersMap.has(key)) { - visitorsHandlersMap.set(key, []); - } - visitorsHandlersMap - .get(key) - .push(value); - } - } - else { - errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); - } - } - else { - errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); - } - } - } if (Array.isArray(iter)) { // @ts-expect-error iter = iter[Symbol.iterator](); @@ -30084,7 +29960,6 @@ async function doParse(iter, options = {}) { ast = expand(ast); } let replacement; - let callable; while (stack.length > 0 && context != ast) { const previousNode = stack.pop(); context = (stack[stack.length - 1] ?? ast); @@ -30100,208 +29975,188 @@ async function doParse(iter, options = {}) { break; } if (options.visitor != null) { + valuesHandlers = new Map(); + preValuesHandlers = new Map(); + postValuesHandlers = new Map(); + preVisitorsHandlersMap = new Map(); + visitorsHandlersMap = new Map(); + postVisitorsHandlersMap = new Map(); + parseVisitors(options, valuesHandlers, preValuesHandlers, postValuesHandlers, errors, visitorsHandlersMap, preVisitorsHandlersMap, postVisitorsHandlersMap); let parens; - for (const result of walk(ast)) { + let genericKey; + const handlers = []; + const allHandlers = []; + if (preVisitorsHandlersMap.size > 0) { + allHandlers.push(preVisitorsHandlersMap); + } + if (preValuesHandlers.size > 0) { + allHandlers.push(preValuesHandlers); + } + if (visitorsHandlersMap.size > 0) { + allHandlers.push(visitorsHandlersMap); + } + if (valuesHandlers.size > 0) { + allHandlers.push(valuesHandlers); + } + if (postVisitorsHandlersMap.size > 0) { + allHandlers.push(postVisitorsHandlersMap); + } + if (postValuesHandlers.size > 0) { + allHandlers.push(postValuesHandlers); + } + let nodes = new Array(stats.tokensCount); + const subNodes = []; + let i; + let k; + let j; + let freeblock = 1; + const includeTokens = preValuesHandlers.size > 0 || valuesHandlers.size > 0 || postValuesHandlers.size > 0; + nodes[0] = ast; + for (i = 0; i < nodes.length; i++) { + if (nodes[i] == null) { + break; + } + subNodes.length = 0; + if (includeTokens) { + switch (nodes[i].typ) { + case exports.EnumToken.RuleNodeType: + case exports.EnumToken.AtRuleNodeType: + case exports.EnumToken.KeyframesRuleNodeType: + case exports.EnumToken.KeyframesAtRuleNodeType: + subNodes.push(...nodes[i][TOKENS]); + break; + case exports.EnumToken.DeclarationNodeType: + subNodes.push(...nodes[i].val); + break; + } + } + if (nodes[i].chi != null) { + subNodes.push(...nodes[i].chi); + } + if (subNodes.length > 0) { + if (freeblock <= i) { + freeblock = i + 1; + } + for (k = 0; k < subNodes.length; k++) { + j = k + freeblock; + nodes[j] = subNodes[k]; + nodes[j][PARENT] = nodes[i]; + } + freeblock += subNodes.length; + } parens = null; - if (valuesHandlers.size > 0 || - preVisitorsHandlersMap.size > 0 || - visitorsHandlersMap.size > 0 || - postVisitorsHandlersMap.size > 0) { - if ((result.node.typ == exports.EnumToken.DeclarationNodeType && - (preVisitorsHandlersMap.has("Declaration") || - visitorsHandlersMap.has("Declaration") || - postVisitorsHandlersMap.has("Declaration"))) || - (result.node.typ == exports.EnumToken.AtRuleNodeType && - (preVisitorsHandlersMap.has("AtRule") || - visitorsHandlersMap.has("AtRule") || - postVisitorsHandlersMap.has("AtRule"))) || - (result.node.typ == exports.EnumToken.KeyframesAtRuleNodeType && - (preVisitorsHandlersMap.has("KeyframesAtRule") || - visitorsHandlersMap.has("KeyframesAtRule") || - postVisitorsHandlersMap.has("KeyframesAtRule")))) { - const handlers = []; - const key = result.node.typ == exports.EnumToken.DeclarationNodeType - ? "Declaration" - : result.node.typ == exports.EnumToken.AtRuleNodeType - ? "AtRule" - : "KeyframesAtRule"; - if (preVisitorsHandlersMap.has(key)) { - handlers.push( - // @ts-expect-error - ...preVisitorsHandlersMap.get(key)); - } - if (visitorsHandlersMap.has(key)) { - // @ts-ignore - handlers.push(...visitorsHandlersMap.get(key)); - } - if (postVisitorsHandlersMap.has(key)) { - // @ts-ignore - handlers.push(...postVisitorsHandlersMap.get(key)); - } - let node = result.node; - for (const handler of handlers) { - callable = - typeof handler == "function" - ? handler - : handler[camelize(node.typ === exports.EnumToken.DeclarationNodeType || - node.typ === exports.EnumToken.AtRuleNodeType - ? node.nam - : node.val)]; - if (callable == null) { - continue; + handlers.length = 0; + genericKey = + nodes[i].typ == exports.EnumToken.DeclarationNodeType + ? "Declaration" + : nodes[i].typ == exports.EnumToken.AtRuleNodeType + ? "AtRule" + : nodes[i].typ == exports.EnumToken.KeyframesAtRuleNodeType + ? "KeyframesAtRule" + : nodes[i].typ === exports.EnumToken.KeyframesRuleNodeType + ? "KeyframesRule" + : nodes[i].typ == exports.EnumToken.RuleNodeType + ? "Rule" + : nodes[i].typ == exports.EnumToken.KeyframesRuleNodeType + ? "KeyframesRule" + : null; + let keyName = nodes[i].typ == exports.EnumToken.DeclarationNodeType || nodes[i].typ == exports.EnumToken.AtRuleNodeType + ? camelize(nodes[i].nam) + : nodes[i].typ == exports.EnumToken.KeyframesAtRuleNodeType + ? camelize(nodes[i].val) + : null; + for (const map of allHandlers) { + // @ts-ignore + if (genericKey != null && map.has(genericKey)) { + // @ts-ignore + for (const handler of map.get(genericKey)) { + if (typeof handler == "function") { + handlers.push(handler); } - // @ts-expect-error - replacement = callable(node, result[PARENT], ast, function* () { - if (parens == null) { - // @ts-expect-error - parens = [...result.parents()]; + else if (Array.isArray(handler)) { + for (const h of handler) { + if (typeof h == "function") { + handlers.push(h); + } + // @ts-ignore + else if (h[keyName] != null) { + // @ts-ignore + handlers.push(h[keyName]); + } } - yield* parens[Symbol.iterator](); - }); - if (replacement == null) { - continue; - } - if (replacement instanceof Promise) { - replacement = await replacement; } - if (replacement == null || replacement == node) { - continue; + else if (typeof handler.handler == "function") { + handlers.push(handler.handler); } // @ts-ignore - node = replacement; - if (Array.isArray(node)) { - break; + else if (typeof handler[keyName] == "function") { + // @ts-ignore + handlers.push(handler[keyName]); } } - if (node != result.node) { - replaceNodeOrValue(result.parent, result.node, node); - } } - else if ((result.node.typ == exports.EnumToken.RuleNodeType && - (preVisitorsHandlersMap.has("Rule") || - visitorsHandlersMap.has("Rule") || - postVisitorsHandlersMap.has("Rule"))) || - (result.node.typ == exports.EnumToken.KeyFramesRuleNodeType && - (preVisitorsHandlersMap.has("KeyframesRule") || - visitorsHandlersMap.has("KeyframesRule") || - postVisitorsHandlersMap.has("KeyframesRule")))) { - const handlers = []; - const key = result.node.typ == exports.EnumToken.RuleNodeType ? "Rule" : "KeyframesRule"; - if (preVisitorsHandlersMap.has(key)) { - handlers.push(...preVisitorsHandlersMap.get(key)); - } - if (visitorsHandlersMap.has(key)) { - handlers.push(...visitorsHandlersMap.get(key)); - } - if (postVisitorsHandlersMap.has(key)) { - handlers.push(...postVisitorsHandlersMap.get(key)); - } - let node = result.node; - for (const callable of handlers) { - replacement = callable(node, result.parent, result.root, - // @ts-expect-error - function* () { - if (parens == null) { - // @ts-expect-error - parens = [...result.parents()]; - } - yield* parens[Symbol.iterator](); - }); - if (replacement == null) { - continue; + // @ts-ignore + if (map.has(nodes[i].typ)) { + // @ts-ignore + for (const handler of map.get(nodes[i].typ)) { + if (typeof handler == "function") { + handlers.push(handler); } - if (replacement instanceof Promise) { - replacement = await replacement; + else if (Array.isArray(handler)) { + for (const h of handler) { + if (typeof h == "function") { + handlers.push(h); + } + } } - if (replacement == null || replacement == node) { - continue; + else if (typeof handler.handler == "function") { + handlers.push(handler.handler); } // @ts-ignore - node = replacement; - // - if (Array.isArray(node)) { - break; + else if (typeof handler[keyName] == "function") { + // @ts-ignore + handlers.push(handler[keyName]); } } - // @ts-ignore - if (node != result.node) { - // @ts-ignore - replaceNodeOrValue(result.parent, result.node, node); - } } - else if (valuesHandlers.size > 0) { - let node = null; - node = result.node; - if (valuesHandlers.has(node.typ)) { - for (const valueHandler of valuesHandlers.get(node.typ)) { - callable = valueHandler; - replacement = callable(node, result.parent, ast, - // @ts-expect-error - function* () { - if (parens == null) { - // @ts-expect-error - parens = [...result.parents()]; - } - yield* parens[Symbol.iterator](); - }); - if (replacement == null) { - continue; - } - if (replacement instanceof Promise) { - replacement = await replacement; - } - if (replacement != null && replacement != node) { - node = replacement; - } - } - } - if (node != result.node) { - // @ts-ignore - replaceNodeOrValue(result[PARENT], value, node); - } - const tokens = Array.isArray(result.node[TOKENS]) ? result.node[TOKENS] : []; - if (Array.isArray(result.node.val)) { - tokens.push(...result.node.val); - } - if (tokens.length == 0) { - continue; - } - for (const { value, parent, root, parents } of walkValues(tokens, result.node)) { - node = value; - if (valuesHandlers.has(node.typ)) { - let parens = null; - for (const valueHandler of valuesHandlers.get(node.typ)) { - callable = valueHandler; - // @ts-expect-error - let result = callable(node, parent, root, function* () { - if (parens == null) { - // @ts-expect-error - parens = [...parents()]; - } - yield* parens[Symbol.iterator](); - }); - if (result == null) { - continue; - } - if (result instanceof Promise) { - result = await result; - } - if (result != null && result != node) { - node = result; - } - if (Array.isArray(node)) { - break; - } - } - } - if (node != value) { - // @ts-ignore - replaceNodeOrValue(parent, value, node); + } + if (handlers.length == 0) { + continue; + } + let node = nodes[i]; + for (const callable of handlers) { + replacement = callable(node, nodes[i][PARENT], ast, + // @ts-expect-error + function* () { + if (parens == null) { + let node = nodes[i][PARENT]; + while (node != null) { + yield node; + node = node[PARENT]; } } + }); + if (replacement == null) { + continue; + } + if (replacement instanceof Promise) { + replacement = await replacement; + } + if (replacement == null || replacement == node) { + continue; + } + // @ts-ignore + node = replacement; + // + if (Array.isArray(node)) { + break; } } + if (node != nodes[i]) { + replaceNodeOrValue(nodes[i][PARENT], nodes[i], node); + } } + nodes = null; } if (invalidNodes.length > 0) { let count = invalidNodes.length; diff --git a/dist/index.d.ts b/dist/index.d.ts index ae5c71fd..549223a7 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -379,7 +379,7 @@ declare enum EnumToken { /** * keyframe rule node type */ - KeyFramesRuleNodeType = 73, + KeyframesRuleNodeType = 73, /** * class selector token type */ @@ -2937,7 +2937,7 @@ export declare interface AstKeyframesRule extends BaseToken, AstNodeStatus { /** * token type */ - typ: EnumToken.KeyFramesRuleNodeType; + typ: EnumToken.KeyframesRuleNodeType; /** * selector */ @@ -2967,7 +2967,7 @@ export declare interface AstKeyframesRule extends BaseToken, AstNodeStatus { /** * token type */ - typ: EnumToken.KeyFramesRuleNodeType; + typ: EnumToken.KeyframesRuleNodeType; /** * selector */ diff --git a/dist/lib/ast/features/shorthand.js b/dist/lib/ast/features/shorthand.js index 4c3fcfee..2843a483 100644 --- a/dist/lib/ast/features/shorthand.js +++ b/dist/lib/ast/features/shorthand.js @@ -6,7 +6,7 @@ class ComputeShorthandFeature { accept = new Set([ EnumToken.RuleNodeType, EnumToken.AtRuleNodeType, - EnumToken.KeyFramesRuleNodeType, + EnumToken.KeyframesRuleNodeType, ]); get ordering() { return 10; diff --git a/dist/lib/ast/features/transform.js b/dist/lib/ast/features/transform.js index 63eed655..0009cfe3 100644 --- a/dist/lib/ast/features/transform.js +++ b/dist/lib/ast/features/transform.js @@ -6,7 +6,7 @@ import { FeatureWalkMode } from './type.js'; import { STATE } from '../../syntax/constants.js'; class TransformCssFeature { - accept = new Set([EnumToken.RuleNodeType, EnumToken.KeyFramesRuleNodeType]); + accept = new Set([EnumToken.RuleNodeType, EnumToken.KeyframesRuleNodeType]); get ordering() { return 3; } diff --git a/dist/lib/ast/minify.js b/dist/lib/ast/minify.js index 896e8ec6..11b2dac5 100644 --- a/dist/lib/ast/minify.js +++ b/dist/lib/ast/minify.js @@ -17,7 +17,7 @@ const rules = [ EnumToken.AtRuleNodeType, EnumToken.RuleNodeType, EnumToken.AtRuleTokenType, - EnumToken.KeyFramesRuleNodeType, + EnumToken.KeyframesRuleNodeType, ]; // @ts-ignore const features = Object.values(index).sort((a, b) => a.ordering - b.ordering); @@ -75,7 +75,7 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent, co continue; } if (rules.includes(replacement.typ) && !Array.isArray(replacement[TOKENS])) { - replacement[TOKENS] = parseString(replacement.typ == EnumToken.RuleNodeType || replacement.typ === EnumToken.KeyFramesRuleNodeType + replacement[TOKENS] = parseString(replacement.typ == EnumToken.RuleNodeType || replacement.typ === EnumToken.KeyframesRuleNodeType ? replacement.sel : replacement.nam); } @@ -350,8 +350,8 @@ function doMinify(ast, options = {}, recursive = false, errors, nestingContent, continue; } } - else if (node.typ === EnumToken.KeyFramesRuleNodeType) { - if (previous?.typ === EnumToken.KeyFramesRuleNodeType && + else if (node.typ === EnumToken.KeyframesRuleNodeType) { + if (previous?.typ === EnumToken.KeyframesRuleNodeType && node.sel === previous.sel) { // do not merge keyframes // https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@keyframes#resolving_duplicates @@ -639,7 +639,7 @@ function doMinify(ast, options = {}, recursive = false, errors, nestingContent, } if (shouldMerge) { if (((node.typ === EnumToken.RuleNodeType || - node.typ === EnumToken.KeyFramesRuleNodeType) && + node.typ === EnumToken.KeyframesRuleNodeType) && node.sel === previous.sel) || (node.typ == EnumToken.AtRuleNodeType && node.nam !== "font-face" && @@ -652,7 +652,7 @@ function doMinify(ast, options = {}, recursive = false, errors, nestingContent, continue; } else if (node.typ == previous?.typ && - [EnumToken.KeyFramesRuleNodeType, EnumToken.RuleNodeType].includes(node.typ)) { + [EnumToken.KeyframesRuleNodeType, EnumToken.RuleNodeType].includes(node.typ)) { const intersect = diff(previous, node, options); if (intersect != null) { if (intersect.node1.chi.length == 0) { diff --git a/dist/lib/ast/types.js b/dist/lib/ast/types.js index 083d23c9..3786dc5c 100644 --- a/dist/lib/ast/types.js +++ b/dist/lib/ast/types.js @@ -385,7 +385,7 @@ var EnumToken; /** * keyframe rule node type */ - EnumToken[EnumToken["KeyFramesRuleNodeType"] = 73] = "KeyFramesRuleNodeType"; + EnumToken[EnumToken["KeyframesRuleNodeType"] = 73] = "KeyframesRuleNodeType"; /** * class selector token type */ diff --git a/dist/lib/parser/parse.js b/dist/lib/parser/parse.js index 64cc0afa..3dffa43c 100644 --- a/dist/lib/parser/parse.js +++ b/dist/lib/parser/parse.js @@ -4,7 +4,7 @@ import { renderValue } from '../renderer/render.js'; import { EnumToken, EnumAstNodeStatus, ModuleCaseTransformEnum, ModuleScopeEnumOptions } from '../ast/types.js'; import { minify } from '../ast/minify.js'; import { expand } from '../ast/expand.js'; -import { WalkerEvent, walk, walkValues } from '../ast/walk.js'; +import { walk, walkValues, WalkerEvent } from '../ast/walk.js'; import { tokenizeStream, tokenize } from './tokenize.js'; import { LOC, tokensfuncDefMap, STATE, PARENT, TOKENS, ROOT, ERRORS, pageMarginBoxType } from '../syntax/constants.js'; import { hashAlgorithms, hash, syncHash } from './utils/hash.js'; @@ -291,6 +291,99 @@ const generateSyncScopedName = memoize((localName, filePath, pattern, hashLength // if leading char is digit, prefix underscore (very rare) return (/^[0-9]/.test(result) ? "_" : "") + result; }); +function parseVisitors(options, valuesHandlers, preValuesHandlers, postValuesHandlers, errors, visitorsHandlersMap, preVisitorsHandlersMap, postVisitorsHandlersMap) { + const visitors = Object.entries(options.visitor); + let key; + let value; + let i; + for (i = 0; i < visitors.length; i++) { + key = visitors[i][0]; + value = visitors[i][1]; + if (Number.isInteger(+key)) { + if (Array.isArray(value)) { + visitors.splice(i + 1, 0, ...Object.entries(value)); + continue; + } + if (typeof value == "function") { + key = value.name; + } + } + if (Array.isArray(value)) { + // @ts-ignore + visitors.splice(i + 1, 0, ...value.map((item) => [key, item])); + continue; + } + if (key in EnumToken) { + if (typeof value == "function") { + if (!valuesHandlers.has(EnumToken[key])) { + valuesHandlers.set(EnumToken[key], []); + } + valuesHandlers.get(EnumToken[key]).push(value); + } + else if (typeof value == "object" && "type" in value && "handler" in value && value.type in WalkerEvent) { + if (value.type == WalkerEvent.Enter) { + if (!preValuesHandlers.has(EnumToken[key])) { + preValuesHandlers.set(EnumToken[key], []); + } + preValuesHandlers.get(EnumToken[key]).push(value.handler); + } + else if (value.type == WalkerEvent.Leave) { + if (!postValuesHandlers.has(EnumToken[key])) { + postValuesHandlers.set(EnumToken[key], []); + } + postValuesHandlers.get(EnumToken[key]).push(value.handler); + } + } + else { + errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); + } + } + else if (["Declaration", "Rule", "AtRule", "KeyframesRule", "KeyframesAtRule"].includes(key)) { + if (typeof value == "function") { + if (!visitorsHandlersMap.has(key)) { + visitorsHandlersMap.set(key, []); + } + visitorsHandlersMap + .get(key) + .push(value); + } + else if (typeof value == "object") { + if ("type" in value && "handler" in value && value.type in WalkerEvent) { + if (value.type == WalkerEvent.Enter) { + if (!preVisitorsHandlersMap.has(key)) { + preVisitorsHandlersMap.set(key, []); + } + preVisitorsHandlersMap + .get(key) + .push(value.handler); + } + else if (value.type == WalkerEvent.Leave) { + if (!postVisitorsHandlersMap.has(key)) { + postVisitorsHandlersMap.set(key, []); + } + postVisitorsHandlersMap + .get(key) + .push(value.handler); + } + } + else { + if (!visitorsHandlersMap.has(key)) { + visitorsHandlersMap.set(key, []); + } + visitorsHandlersMap + .get(key) + .push(value); + } + } + else { + errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); + } + } + else { + errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); + } + } +} /** * Parse css string * @param iter @@ -373,114 +466,13 @@ function doParseSync(iter, options = {}) { // @ts-ignore ignore error let parensMatch = 0; let curlyBracketMatch = 0; - if (options.visitor != null) { - valuesHandlers = new Map(); - preValuesHandlers = new Map(); - postValuesHandlers = new Map(); - preVisitorsHandlersMap = new Map(); - visitorsHandlersMap = new Map(); - postVisitorsHandlersMap = new Map(); - const visitors = Object.entries(options.visitor); - let key; - let value; - let i; - for (i = 0; i < visitors.length; i++) { - key = visitors[i][0]; - value = visitors[i][1]; - if (Number.isInteger(+key)) { - visitors.splice(i + 1, 0, ...Object.entries(value)); - continue; - } - if (Array.isArray(value)) { - // @ts-ignore - visitors.splice(i + 1, 0, ...value.map((item) => [key, item])); - continue; - } - if (key in EnumToken) { - if (typeof value == "function") { - if (!valuesHandlers.has(EnumToken[key])) { - valuesHandlers.set(EnumToken[key], []); - } - valuesHandlers.get(EnumToken[key]).push(value); - } - else if (typeof value == "object" && - "type" in value && - "handler" in value && - value.type in WalkerEvent) { - if (value.type == WalkerEvent.Enter) { - if (!preValuesHandlers.has(EnumToken[key])) { - preValuesHandlers.set(EnumToken[key], []); - } - preValuesHandlers - .get(EnumToken[key]) - .push(value.handler); - } - else if (value.type == WalkerEvent.Leave) { - if (!postValuesHandlers.has(EnumToken[key])) { - postValuesHandlers.set(EnumToken[key], []); - } - postValuesHandlers - .get(EnumToken[key]) - .push(value.handler); - } - } - else { - errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); - } - } - else if (["Declaration", "Rule", "AtRule", "KeyframesRule", "KeyframesAtRule"].includes(key)) { - if (typeof value == "function") { - if (!visitorsHandlersMap.has(key)) { - visitorsHandlersMap.set(key, []); - } - visitorsHandlersMap - .get(key) - .push(value); - } - else if (typeof value == "object") { - if ("type" in value && "handler" in value && value.type in WalkerEvent) { - if (value.type == WalkerEvent.Enter) { - if (!preVisitorsHandlersMap.has(key)) { - preVisitorsHandlersMap.set(key, []); - } - preVisitorsHandlersMap - .get(key) - .push(value.handler); - } - else if (value.type == WalkerEvent.Leave) { - if (!postVisitorsHandlersMap.has(key)) { - postVisitorsHandlersMap.set(key, []); - } - postVisitorsHandlersMap - .get(key) - .push(value.handler); - } - } - else { - if (!visitorsHandlersMap.has(key)) { - visitorsHandlersMap.set(key, []); - } - visitorsHandlersMap - .get(key) - .push(value); - } - } - else { - errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); - } - } - else { - errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); - } - } - } - if (Array.isArray(iter)) { - // @ts-expect-error - iter = iter[Symbol.iterator](); - } - while ( - // @ts-expect-error - (item = iter.next().value)) { + let currentItemIndex; + // if (Array.isArray(iter)) { + // // @ts-expect-error + // iter = iter[Symbol.iterator]() as Iterator; + // } + for (currentItemIndex = 0; currentItemIndex < iter.length; currentItemIndex++) { + item = iter[currentItemIndex]; stats.bytesIn = item.bytesIn; stats.tokensCount++; if (BadTokensTypes.includes(item.token.typ)) { @@ -508,8 +500,6 @@ function doParseSync(iter, options = {}) { curlyBracketMatch--; } tokens.push(item.token); - // console.debug([item.token, {parensMatch, curlyBracketMatch}]); - // if (parensMatch === 0) { if (parensMatch === 0 && (item.token.typ === EnumToken.SemiColonTokenType || item.token.typ === EnumToken.BlockStartTokenType || @@ -525,8 +515,7 @@ function doParseSync(iter, options = {}) { let inBlock = 1; tokens = [item.token]; do { - // @ts-expect-error - item = iter.next().value; + item = iter[++currentItemIndex]; if (item == null) { break; } @@ -581,198 +570,186 @@ function doParseSync(iter, options = {}) { ast = expand(ast); } let replacement; - let callable; if (options.visitor != null) { + valuesHandlers = new Map(); + preValuesHandlers = new Map(); + postValuesHandlers = new Map(); + preVisitorsHandlersMap = new Map(); + visitorsHandlersMap = new Map(); + postVisitorsHandlersMap = new Map(); + parseVisitors(options, valuesHandlers, preValuesHandlers, postValuesHandlers, errors, visitorsHandlersMap, preVisitorsHandlersMap, postVisitorsHandlersMap); let parens; - for (const result of walk(ast)) { + let genericKey; + const handlers = []; + const allHandlers = []; + if (preVisitorsHandlersMap.size > 0) { + allHandlers.push(preVisitorsHandlersMap); + } + if (preValuesHandlers.size > 0) { + allHandlers.push(preValuesHandlers); + } + if (visitorsHandlersMap.size > 0) { + allHandlers.push(visitorsHandlersMap); + } + if (valuesHandlers.size > 0) { + allHandlers.push(valuesHandlers); + } + if (postVisitorsHandlersMap.size > 0) { + allHandlers.push(postVisitorsHandlersMap); + } + if (postValuesHandlers.size > 0) { + allHandlers.push(postValuesHandlers); + } + let nodes = new Array(stats.tokensCount); + const subNodes = []; + let i; + let k; + let j; + let freeBlock = 1; + const includeTokens = preValuesHandlers.size > 0 || valuesHandlers.size > 0 || postValuesHandlers.size > 0; + nodes[0] = ast; + for (i = 0; i < nodes.length; i++) { + if (nodes[i] == null) { + break; + } + subNodes.length = 0; + if (includeTokens) { + switch (nodes[i].typ) { + case EnumToken.RuleNodeType: + case EnumToken.AtRuleNodeType: + case EnumToken.KeyframesRuleNodeType: + case EnumToken.KeyframesAtRuleNodeType: + subNodes.push(...nodes[i][TOKENS]); + break; + case EnumToken.DeclarationNodeType: + subNodes.push(...nodes[i].val); + break; + } + } + if (nodes[i].chi != null) { + subNodes.push(...nodes[i].chi); + } + if (subNodes.length > 0) { + if (freeBlock <= i) { + freeBlock = i + 1; + } + for (k = 0; k < subNodes.length; k++) { + j = k + freeBlock; + nodes[j] = subNodes[k]; + nodes[j][PARENT] = nodes[i]; + } + freeBlock += subNodes.length; + } parens = null; - if (valuesHandlers.size > 0 || - preVisitorsHandlersMap.size > 0 || - visitorsHandlersMap.size > 0 || - postVisitorsHandlersMap.size > 0) { - if ((result.node.typ == EnumToken.DeclarationNodeType && - (preVisitorsHandlersMap.has("Declaration") || - visitorsHandlersMap.has("Declaration") || - postVisitorsHandlersMap.has("Declaration"))) || - (result.node.typ == EnumToken.AtRuleNodeType && - (preVisitorsHandlersMap.has("AtRule") || - visitorsHandlersMap.has("AtRule") || - postVisitorsHandlersMap.has("AtRule"))) || - (result.node.typ == EnumToken.KeyframesAtRuleNodeType && - (preVisitorsHandlersMap.has("KeyframesAtRule") || - visitorsHandlersMap.has("KeyframesAtRule") || - postVisitorsHandlersMap.has("KeyframesAtRule")))) { - const handlers = []; - const key = result.node.typ == EnumToken.DeclarationNodeType - ? "Declaration" - : result.node.typ == EnumToken.AtRuleNodeType - ? "AtRule" - : "KeyframesAtRule"; - if (preVisitorsHandlersMap.has(key)) { - handlers.push( - // @ts-expect-error - ...preVisitorsHandlersMap.get(key)); - } - if (visitorsHandlersMap.has(key)) { - // @ts-ignore - handlers.push(...visitorsHandlersMap.get(key)); - } - if (postVisitorsHandlersMap.has(key)) { - // @ts-ignore - handlers.push(...postVisitorsHandlersMap.get(key)); - } - let node = result.node; - for (const handler of handlers) { - callable = - typeof handler == "function" - ? handler - : handler[camelize(node.typ === EnumToken.DeclarationNodeType || - node.typ === EnumToken.AtRuleNodeType - ? node.nam - : node.val)]; - if (callable == null) { - continue; - } - // @ts-expect-error - replacement = callable(node, result[PARENT], ast, function* () { - if (parens == null) { - // @ts-expect-error - parens = [...result.parents()]; + handlers.length = 0; + genericKey = + nodes[i].typ == EnumToken.DeclarationNodeType + ? "Declaration" + : nodes[i].typ == EnumToken.AtRuleNodeType + ? "AtRule" + : nodes[i].typ == EnumToken.KeyframesAtRuleNodeType + ? "KeyframesAtRule" + : nodes[i].typ === EnumToken.KeyframesRuleNodeType + ? "KeyframesRule" + : nodes[i].typ == EnumToken.RuleNodeType + ? "Rule" + : nodes[i].typ == EnumToken.KeyframesRuleNodeType + ? "KeyframesRule" + : null; + let keyName = nodes[i].typ == EnumToken.DeclarationNodeType || nodes[i].typ == EnumToken.AtRuleNodeType + ? camelize(nodes[i].nam) + : nodes[i].typ == EnumToken.KeyframesAtRuleNodeType + ? camelize(nodes[i].val) + : null; + for (const map of allHandlers) { + // @ts-ignore + if (genericKey != null && map.has(genericKey)) { + // @ts-ignore + for (const handler of map.get(genericKey)) { + if (typeof handler == "function") { + handlers.push(handler); + } + else if (Array.isArray(handler)) { + for (const h of handler) { + if (typeof h == "function") { + handlers.push(h); + } + // @ts-ignore + else if (h[keyName] != null) { + // @ts-ignore + handlers.push(h[keyName]); + } } - yield* parens[Symbol.iterator](); - }); - if (replacement == null) { - continue; } - if (replacement == node) { - continue; + else if (typeof handler.handler == "function") { + handlers.push(handler.handler); } // @ts-ignore - node = replacement; - if (Array.isArray(node)) { - break; + else if (typeof handler[keyName] == "function") { + // @ts-ignore + handlers.push(handler[keyName]); } } - if (node != result.node) { - replaceNodeOrValue(result.parent, result.node, node); - } - } - else if ((result.node.typ == EnumToken.RuleNodeType && - (preVisitorsHandlersMap.has("Rule") || - visitorsHandlersMap.has("Rule") || - postVisitorsHandlersMap.has("Rule"))) || - (result.node.typ == EnumToken.KeyFramesRuleNodeType && - (preVisitorsHandlersMap.has("KeyframesRule") || - visitorsHandlersMap.has("KeyframesRule") || - postVisitorsHandlersMap.has("KeyframesRule")))) { - const handlers = []; - const key = result.node.typ == EnumToken.RuleNodeType ? "Rule" : "KeyframesRule"; - if (preVisitorsHandlersMap.has(key)) { - handlers.push(...preVisitorsHandlersMap.get(key)); - } - if (visitorsHandlersMap.has(key)) { - handlers.push(...visitorsHandlersMap.get(key)); - } - if (postVisitorsHandlersMap.has(key)) { - handlers.push(...postVisitorsHandlersMap.get(key)); - } - let node = result.node; - for (const callable of handlers) { - replacement = callable(node, result.parent, result.root, - // @ts-expect-error - function* () { - if (parens == null) { - // @ts-expect-error - parens = [...result.parents()]; + } + // @ts-ignore + if (map.has(nodes[i].typ)) { + // @ts-ignore + for (const handler of map.get(nodes[i].typ)) { + if (typeof handler == "function") { + handlers.push(handler); + } + else if (Array.isArray(handler)) { + for (const h of handler) { + if (typeof h == "function") { + handlers.push(h); + } } - yield* parens[Symbol.iterator](); - }); - if (replacement == null) { - continue; } - if (replacement == node) { - continue; + else if (typeof handler.handler == "function") { + handlers.push(handler.handler); } // @ts-ignore - node = replacement; - // - if (Array.isArray(node)) { - break; + else if (typeof handler[keyName] == "function") { + // @ts-ignore + handlers.push(handler[keyName]); } } - // @ts-ignore - if (node != result.node) { - // @ts-ignore - replaceNodeOrValue(result.parent, result.node, node); - } } - else if (valuesHandlers.size > 0) { - let node = null; - node = result.node; - if (valuesHandlers.has(node.typ)) { - for (const valueHandler of valuesHandlers.get(node.typ)) { - callable = valueHandler; - replacement = callable(node, result.parent, ast, - // @ts-expect-error - function* () { - if (parens == null) { - // @ts-expect-error - parens = [...result.parents()]; - } - yield* parens[Symbol.iterator](); - }); - if (replacement == null) { - continue; - } - if (replacement != node) { - node = replacement; - } - } - } - if (node != result.node) { - // @ts-ignore - replaceNodeOrValue(result[PARENT], value, node); - } - const tokens = Array.isArray(result.node[TOKENS]) ? result.node[TOKENS] : []; - if (Array.isArray(result.node.val)) { - tokens.push(...result.node.val); - } - if (tokens.length == 0) { - continue; - } - for (const { value, parent, root, parents } of walkValues(tokens, result.node)) { - node = value; - if (valuesHandlers.has(node.typ)) { - let parens = null; - for (const valueHandler of valuesHandlers.get(node.typ)) { - callable = valueHandler; - // @ts-expect-error - let result = callable(node, parent, root, function* () { - if (parens == null) { - // @ts-expect-error - parens = [...parents()]; - } - yield* parens[Symbol.iterator](); - }); - if (result == null) { - continue; - } - if (result != node) { - node = result; - } - if (Array.isArray(node)) { - break; - } - } - } - if (node != value) { - // @ts-ignore - replaceNodeOrValue(parent, value, node); + } + if (handlers.length == 0) { + continue; + } + let node = nodes[i]; + for (const callable of handlers) { + replacement = callable(node, nodes[i][PARENT], ast, + // @ts-expect-error + function* () { + if (parens == null) { + let node = nodes[i][PARENT]; + while (node != null) { + yield node; + node = node[PARENT]; } } + }); + if (replacement == null) { + continue; } + if (replacement == node) { + continue; + } + // @ts-ignore + node = replacement; + // + if (Array.isArray(node)) { + break; + } + } + if (node != nodes[i]) { + replaceNodeOrValue(nodes[i][PARENT], nodes[i], node); } } + nodes = null; } if (invalidNodes.length > 0) { let count = invalidNodes.length; @@ -836,7 +813,7 @@ function doParseSync(iter, options = {}) { scoped: ModuleScopeEnumOptions.Local, naming: ModuleCaseTransformEnum.IgnoreCase, pattern: "", - generateScopedName, + generateScopedName: generateSyncScopedName, ...(typeof options.module != "object" ? {} : options.module), }; const parseModuleTime = performance.now(); @@ -1399,107 +1376,6 @@ async function doParse(iter, options = {}) { let isAsync = typeof iter[Symbol.asyncIterator] === "function"; let parensMatch = 0; let curlyBracketMatch = 0; - if (options.visitor != null) { - valuesHandlers = new Map(); - preValuesHandlers = new Map(); - postValuesHandlers = new Map(); - preVisitorsHandlersMap = new Map(); - visitorsHandlersMap = new Map(); - postVisitorsHandlersMap = new Map(); - const visitors = Object.entries(options.visitor); - let key; - let value; - let i; - for (i = 0; i < visitors.length; i++) { - key = visitors[i][0]; - value = visitors[i][1]; - if (Number.isInteger(+key)) { - visitors.splice(i + 1, 0, ...Object.entries(value)); - continue; - } - if (Array.isArray(value)) { - // @ts-ignore - visitors.splice(i + 1, 0, ...value.map((item) => [key, item])); - continue; - } - if (key in EnumToken) { - if (typeof value == "function") { - if (!valuesHandlers.has(EnumToken[key])) { - valuesHandlers.set(EnumToken[key], []); - } - valuesHandlers.get(EnumToken[key]).push(value); - } - else if (typeof value == "object" && - "type" in value && - "handler" in value && - value.type in WalkerEvent) { - if (value.type == WalkerEvent.Enter) { - if (!preValuesHandlers.has(EnumToken[key])) { - preValuesHandlers.set(EnumToken[key], []); - } - preValuesHandlers - .get(EnumToken[key]) - .push(value.handler); - } - else if (value.type == WalkerEvent.Leave) { - if (!postValuesHandlers.has(EnumToken[key])) { - postValuesHandlers.set(EnumToken[key], []); - } - postValuesHandlers - .get(EnumToken[key]) - .push(value.handler); - } - } - else { - errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); - } - } - else if (["Declaration", "Rule", "AtRule", "KeyframesRule", "KeyframesAtRule"].includes(key)) { - if (typeof value == "function") { - if (!visitorsHandlersMap.has(key)) { - visitorsHandlersMap.set(key, []); - } - visitorsHandlersMap - .get(key) - .push(value); - } - else if (typeof value == "object") { - if ("type" in value && "handler" in value && value.type in WalkerEvent) { - if (value.type == WalkerEvent.Enter) { - if (!preVisitorsHandlersMap.has(key)) { - preVisitorsHandlersMap.set(key, []); - } - preVisitorsHandlersMap - .get(key) - .push(value.handler); - } - else if (value.type == WalkerEvent.Leave) { - if (!postVisitorsHandlersMap.has(key)) { - postVisitorsHandlersMap.set(key, []); - } - postVisitorsHandlersMap - .get(key) - .push(value.handler); - } - } - else { - if (!visitorsHandlersMap.has(key)) { - visitorsHandlersMap.set(key, []); - } - visitorsHandlersMap - .get(key) - .push(value); - } - } - else { - errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); - } - } - else { - errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); - } - } - } if (Array.isArray(iter)) { // @ts-expect-error iter = iter[Symbol.iterator](); @@ -1661,7 +1537,6 @@ async function doParse(iter, options = {}) { ast = expand(ast); } let replacement; - let callable; while (stack.length > 0 && context != ast) { const previousNode = stack.pop(); context = (stack[stack.length - 1] ?? ast); @@ -1677,208 +1552,188 @@ async function doParse(iter, options = {}) { break; } if (options.visitor != null) { + valuesHandlers = new Map(); + preValuesHandlers = new Map(); + postValuesHandlers = new Map(); + preVisitorsHandlersMap = new Map(); + visitorsHandlersMap = new Map(); + postVisitorsHandlersMap = new Map(); + parseVisitors(options, valuesHandlers, preValuesHandlers, postValuesHandlers, errors, visitorsHandlersMap, preVisitorsHandlersMap, postVisitorsHandlersMap); let parens; - for (const result of walk(ast)) { + let genericKey; + const handlers = []; + const allHandlers = []; + if (preVisitorsHandlersMap.size > 0) { + allHandlers.push(preVisitorsHandlersMap); + } + if (preValuesHandlers.size > 0) { + allHandlers.push(preValuesHandlers); + } + if (visitorsHandlersMap.size > 0) { + allHandlers.push(visitorsHandlersMap); + } + if (valuesHandlers.size > 0) { + allHandlers.push(valuesHandlers); + } + if (postVisitorsHandlersMap.size > 0) { + allHandlers.push(postVisitorsHandlersMap); + } + if (postValuesHandlers.size > 0) { + allHandlers.push(postValuesHandlers); + } + let nodes = new Array(stats.tokensCount); + const subNodes = []; + let i; + let k; + let j; + let freeblock = 1; + const includeTokens = preValuesHandlers.size > 0 || valuesHandlers.size > 0 || postValuesHandlers.size > 0; + nodes[0] = ast; + for (i = 0; i < nodes.length; i++) { + if (nodes[i] == null) { + break; + } + subNodes.length = 0; + if (includeTokens) { + switch (nodes[i].typ) { + case EnumToken.RuleNodeType: + case EnumToken.AtRuleNodeType: + case EnumToken.KeyframesRuleNodeType: + case EnumToken.KeyframesAtRuleNodeType: + subNodes.push(...nodes[i][TOKENS]); + break; + case EnumToken.DeclarationNodeType: + subNodes.push(...nodes[i].val); + break; + } + } + if (nodes[i].chi != null) { + subNodes.push(...nodes[i].chi); + } + if (subNodes.length > 0) { + if (freeblock <= i) { + freeblock = i + 1; + } + for (k = 0; k < subNodes.length; k++) { + j = k + freeblock; + nodes[j] = subNodes[k]; + nodes[j][PARENT] = nodes[i]; + } + freeblock += subNodes.length; + } parens = null; - if (valuesHandlers.size > 0 || - preVisitorsHandlersMap.size > 0 || - visitorsHandlersMap.size > 0 || - postVisitorsHandlersMap.size > 0) { - if ((result.node.typ == EnumToken.DeclarationNodeType && - (preVisitorsHandlersMap.has("Declaration") || - visitorsHandlersMap.has("Declaration") || - postVisitorsHandlersMap.has("Declaration"))) || - (result.node.typ == EnumToken.AtRuleNodeType && - (preVisitorsHandlersMap.has("AtRule") || - visitorsHandlersMap.has("AtRule") || - postVisitorsHandlersMap.has("AtRule"))) || - (result.node.typ == EnumToken.KeyframesAtRuleNodeType && - (preVisitorsHandlersMap.has("KeyframesAtRule") || - visitorsHandlersMap.has("KeyframesAtRule") || - postVisitorsHandlersMap.has("KeyframesAtRule")))) { - const handlers = []; - const key = result.node.typ == EnumToken.DeclarationNodeType - ? "Declaration" - : result.node.typ == EnumToken.AtRuleNodeType - ? "AtRule" - : "KeyframesAtRule"; - if (preVisitorsHandlersMap.has(key)) { - handlers.push( - // @ts-expect-error - ...preVisitorsHandlersMap.get(key)); - } - if (visitorsHandlersMap.has(key)) { - // @ts-ignore - handlers.push(...visitorsHandlersMap.get(key)); - } - if (postVisitorsHandlersMap.has(key)) { - // @ts-ignore - handlers.push(...postVisitorsHandlersMap.get(key)); - } - let node = result.node; - for (const handler of handlers) { - callable = - typeof handler == "function" - ? handler - : handler[camelize(node.typ === EnumToken.DeclarationNodeType || - node.typ === EnumToken.AtRuleNodeType - ? node.nam - : node.val)]; - if (callable == null) { - continue; - } - // @ts-expect-error - replacement = callable(node, result[PARENT], ast, function* () { - if (parens == null) { - // @ts-expect-error - parens = [...result.parents()]; + handlers.length = 0; + genericKey = + nodes[i].typ == EnumToken.DeclarationNodeType + ? "Declaration" + : nodes[i].typ == EnumToken.AtRuleNodeType + ? "AtRule" + : nodes[i].typ == EnumToken.KeyframesAtRuleNodeType + ? "KeyframesAtRule" + : nodes[i].typ === EnumToken.KeyframesRuleNodeType + ? "KeyframesRule" + : nodes[i].typ == EnumToken.RuleNodeType + ? "Rule" + : nodes[i].typ == EnumToken.KeyframesRuleNodeType + ? "KeyframesRule" + : null; + let keyName = nodes[i].typ == EnumToken.DeclarationNodeType || nodes[i].typ == EnumToken.AtRuleNodeType + ? camelize(nodes[i].nam) + : nodes[i].typ == EnumToken.KeyframesAtRuleNodeType + ? camelize(nodes[i].val) + : null; + for (const map of allHandlers) { + // @ts-ignore + if (genericKey != null && map.has(genericKey)) { + // @ts-ignore + for (const handler of map.get(genericKey)) { + if (typeof handler == "function") { + handlers.push(handler); + } + else if (Array.isArray(handler)) { + for (const h of handler) { + if (typeof h == "function") { + handlers.push(h); + } + // @ts-ignore + else if (h[keyName] != null) { + // @ts-ignore + handlers.push(h[keyName]); + } } - yield* parens[Symbol.iterator](); - }); - if (replacement == null) { - continue; - } - if (replacement instanceof Promise) { - replacement = await replacement; } - if (replacement == null || replacement == node) { - continue; + else if (typeof handler.handler == "function") { + handlers.push(handler.handler); } // @ts-ignore - node = replacement; - if (Array.isArray(node)) { - break; + else if (typeof handler[keyName] == "function") { + // @ts-ignore + handlers.push(handler[keyName]); } } - if (node != result.node) { - replaceNodeOrValue(result.parent, result.node, node); - } - } - else if ((result.node.typ == EnumToken.RuleNodeType && - (preVisitorsHandlersMap.has("Rule") || - visitorsHandlersMap.has("Rule") || - postVisitorsHandlersMap.has("Rule"))) || - (result.node.typ == EnumToken.KeyFramesRuleNodeType && - (preVisitorsHandlersMap.has("KeyframesRule") || - visitorsHandlersMap.has("KeyframesRule") || - postVisitorsHandlersMap.has("KeyframesRule")))) { - const handlers = []; - const key = result.node.typ == EnumToken.RuleNodeType ? "Rule" : "KeyframesRule"; - if (preVisitorsHandlersMap.has(key)) { - handlers.push(...preVisitorsHandlersMap.get(key)); - } - if (visitorsHandlersMap.has(key)) { - handlers.push(...visitorsHandlersMap.get(key)); - } - if (postVisitorsHandlersMap.has(key)) { - handlers.push(...postVisitorsHandlersMap.get(key)); - } - let node = result.node; - for (const callable of handlers) { - replacement = callable(node, result.parent, result.root, - // @ts-expect-error - function* () { - if (parens == null) { - // @ts-expect-error - parens = [...result.parents()]; + } + // @ts-ignore + if (map.has(nodes[i].typ)) { + // @ts-ignore + for (const handler of map.get(nodes[i].typ)) { + if (typeof handler == "function") { + handlers.push(handler); + } + else if (Array.isArray(handler)) { + for (const h of handler) { + if (typeof h == "function") { + handlers.push(h); + } } - yield* parens[Symbol.iterator](); - }); - if (replacement == null) { - continue; - } - if (replacement instanceof Promise) { - replacement = await replacement; } - if (replacement == null || replacement == node) { - continue; + else if (typeof handler.handler == "function") { + handlers.push(handler.handler); } // @ts-ignore - node = replacement; - // - if (Array.isArray(node)) { - break; + else if (typeof handler[keyName] == "function") { + // @ts-ignore + handlers.push(handler[keyName]); } } - // @ts-ignore - if (node != result.node) { - // @ts-ignore - replaceNodeOrValue(result.parent, result.node, node); - } } - else if (valuesHandlers.size > 0) { - let node = null; - node = result.node; - if (valuesHandlers.has(node.typ)) { - for (const valueHandler of valuesHandlers.get(node.typ)) { - callable = valueHandler; - replacement = callable(node, result.parent, ast, - // @ts-expect-error - function* () { - if (parens == null) { - // @ts-expect-error - parens = [...result.parents()]; - } - yield* parens[Symbol.iterator](); - }); - if (replacement == null) { - continue; - } - if (replacement instanceof Promise) { - replacement = await replacement; - } - if (replacement != null && replacement != node) { - node = replacement; - } - } - } - if (node != result.node) { - // @ts-ignore - replaceNodeOrValue(result[PARENT], value, node); - } - const tokens = Array.isArray(result.node[TOKENS]) ? result.node[TOKENS] : []; - if (Array.isArray(result.node.val)) { - tokens.push(...result.node.val); - } - if (tokens.length == 0) { - continue; - } - for (const { value, parent, root, parents } of walkValues(tokens, result.node)) { - node = value; - if (valuesHandlers.has(node.typ)) { - let parens = null; - for (const valueHandler of valuesHandlers.get(node.typ)) { - callable = valueHandler; - // @ts-expect-error - let result = callable(node, parent, root, function* () { - if (parens == null) { - // @ts-expect-error - parens = [...parents()]; - } - yield* parens[Symbol.iterator](); - }); - if (result == null) { - continue; - } - if (result instanceof Promise) { - result = await result; - } - if (result != null && result != node) { - node = result; - } - if (Array.isArray(node)) { - break; - } - } - } - if (node != value) { - // @ts-ignore - replaceNodeOrValue(parent, value, node); + } + if (handlers.length == 0) { + continue; + } + let node = nodes[i]; + for (const callable of handlers) { + replacement = callable(node, nodes[i][PARENT], ast, + // @ts-expect-error + function* () { + if (parens == null) { + let node = nodes[i][PARENT]; + while (node != null) { + yield node; + node = node[PARENT]; } } + }); + if (replacement == null) { + continue; + } + if (replacement instanceof Promise) { + replacement = await replacement; + } + if (replacement == null || replacement == node) { + continue; } + // @ts-ignore + node = replacement; + // + if (Array.isArray(node)) { + break; + } + } + if (node != nodes[i]) { + replaceNodeOrValue(nodes[i][PARENT], nodes[i], node); } } + nodes = null; } if (invalidNodes.length > 0) { let count = invalidNodes.length; diff --git a/dist/lib/parser/utils/selector.js b/dist/lib/parser/utils/selector.js index 090bf0c1..04b911a1 100644 --- a/dist/lib/parser/utils/selector.js +++ b/dist/lib/parser/utils/selector.js @@ -48,7 +48,7 @@ function parseSelector(tokens, context, options, errors) { return acc; }, [])); return { - typ: EnumToken.KeyFramesRuleNodeType, + typ: EnumToken.KeyframesRuleNodeType, sel: [ ...splitTokenList(trimArray(tokens)).reduce((acc, curr) => { acc.add(curr.reduce((acc, curr) => acc + renderValue(curr, { minify: false }), "")); diff --git a/dist/lib/renderer/render.js b/dist/lib/renderer/render.js index faeb33b9..6c90b117 100644 --- a/dist/lib/renderer/render.js +++ b/dist/lib/renderer/render.js @@ -149,7 +149,7 @@ function updateSourceMap(node, options, cache, sourcemaps, sourceLocation, lines [ EnumToken.RuleNodeType, EnumToken.AtRuleNodeType, - EnumToken.KeyFramesRuleNodeType, + EnumToken.KeyframesRuleNodeType, EnumToken.KeyframesAtRuleNodeType, ].includes(node.typ)) { const source = options.sourcesMap.get(node[LOC].srcId); @@ -287,7 +287,7 @@ function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, erro return children; case EnumToken.AtRuleNodeType: case EnumToken.RuleNodeType: - case EnumToken.KeyFramesRuleNodeType: + case EnumToken.KeyframesRuleNodeType: case EnumToken.KeyframesAtRuleNodeType: if ([EnumToken.AtRuleNodeType, EnumToken.KeyframesAtRuleNodeType].includes(data.typ) && !("chi" in data)) { return `${indent}@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val};`; diff --git a/src/@types/ast.d.ts b/src/@types/ast.d.ts index 1f5efeb3..e018ba9d 100644 --- a/src/@types/ast.d.ts +++ b/src/@types/ast.d.ts @@ -224,7 +224,7 @@ export declare interface AstKeyframesRule extends BaseToken, AstNodeStatus { /** * token type */ - typ: EnumToken.KeyFramesRuleNodeType; + typ: EnumToken.KeyframesRuleNodeType; /** * selector */ @@ -329,7 +329,7 @@ export declare interface AstKeyframesRule extends BaseToken, AstNodeStatus { /** * token type */ - typ: EnumToken.KeyFramesRuleNodeType; + typ: EnumToken.KeyframesRuleNodeType; /** * selector */ diff --git a/src/lib/ast/features/shorthand.ts b/src/lib/ast/features/shorthand.ts index 20df72b3..daf97e2c 100644 --- a/src/lib/ast/features/shorthand.ts +++ b/src/lib/ast/features/shorthand.ts @@ -14,7 +14,7 @@ export class ComputeShorthandFeature { public accept: Set = new Set([ EnumToken.RuleNodeType, EnumToken.AtRuleNodeType, - EnumToken.KeyFramesRuleNodeType, + EnumToken.KeyframesRuleNodeType, ]); get ordering() { diff --git a/src/lib/ast/features/transform.ts b/src/lib/ast/features/transform.ts index 5cb304ed..1c525520 100644 --- a/src/lib/ast/features/transform.ts +++ b/src/lib/ast/features/transform.ts @@ -15,7 +15,7 @@ import { FeatureWalkMode } from "./type.ts"; import { STATE } from "../../syntax/constants.ts"; export class TransformCssFeature { - public accept: Set = new Set([EnumToken.RuleNodeType, EnumToken.KeyFramesRuleNodeType]); + public accept: Set = new Set([EnumToken.RuleNodeType, EnumToken.KeyframesRuleNodeType]); get ordering(): number { return 3; diff --git a/src/lib/ast/find.ts b/src/lib/ast/find.ts index c9933a11..d283ced2 100644 --- a/src/lib/ast/find.ts +++ b/src/lib/ast/find.ts @@ -239,7 +239,7 @@ export function findValue( (ast.typ === EnumToken.StyleSheetNodeType || ast.typ === EnumToken.RuleNodeType || ast.typ === EnumToken.AtRuleNodeType || - ast.typ === EnumToken.KeyFramesRuleNodeType || + ast.typ === EnumToken.KeyframesRuleNodeType || ast.typ === EnumToken.KeyframesAtRuleNodeType) ) { if (Array.isArray(ast[TOKENS])) { diff --git a/src/lib/ast/minify.ts b/src/lib/ast/minify.ts index b5f85ae7..00b4acd3 100644 --- a/src/lib/ast/minify.ts +++ b/src/lib/ast/minify.ts @@ -39,7 +39,7 @@ const rules: EnumToken[] = [ EnumToken.AtRuleNodeType, EnumToken.RuleNodeType, EnumToken.AtRuleTokenType, - EnumToken.KeyFramesRuleNodeType, + EnumToken.KeyframesRuleNodeType, ]; // @ts-ignore const features: MinifyFeature[] = Object.values(allFeatures as Record).sort( @@ -141,7 +141,7 @@ export function minify( if (rules.includes(replacement.typ) && !Array.isArray(replacement[TOKENS])) { replacement[TOKENS] = parseString( - replacement.typ == EnumToken.RuleNodeType || replacement.typ === EnumToken.KeyFramesRuleNodeType + replacement.typ == EnumToken.RuleNodeType || replacement.typ === EnumToken.KeyframesRuleNodeType ? replacement.sel : replacement.nam, ); @@ -520,9 +520,9 @@ function doMinify( continue; } - } else if (node.typ === EnumToken.KeyFramesRuleNodeType) { + } else if (node.typ === EnumToken.KeyframesRuleNodeType) { if ( - previous?.typ === EnumToken.KeyFramesRuleNodeType && + previous?.typ === EnumToken.KeyframesRuleNodeType && (node).sel === (previous).sel ) { // do not merge keyframes @@ -905,7 +905,7 @@ function doMinify( if (shouldMerge) { if ( ((node.typ === EnumToken.RuleNodeType || - node.typ === EnumToken.KeyFramesRuleNodeType) && + node.typ === EnumToken.KeyframesRuleNodeType) && (node as AstRule).sel === (previous as AstRule).sel) || (node.typ == EnumToken.AtRuleNodeType && (node as AstAtRule).nam !== "font-face" && @@ -923,7 +923,7 @@ function doMinify( continue; } else if ( node.typ == previous?.typ && - [EnumToken.KeyFramesRuleNodeType, EnumToken.RuleNodeType].includes(node.typ) + [EnumToken.KeyframesRuleNodeType, EnumToken.RuleNodeType].includes(node.typ) ) { const intersect = diff(previous as AstRule, node as AstRule, options); diff --git a/src/lib/ast/types.ts b/src/lib/ast/types.ts index 200c50a9..8e9d4c6c 100644 --- a/src/lib/ast/types.ts +++ b/src/lib/ast/types.ts @@ -386,7 +386,7 @@ export enum EnumToken { /** * keyframe rule node type */ - KeyFramesRuleNodeType, + KeyframesRuleNodeType, /** * class selector token type */ diff --git a/src/lib/parser/parse.ts b/src/lib/parser/parse.ts index 719ad5fb..fb993136 100644 --- a/src/lib/parser/parse.ts +++ b/src/lib/parser/parse.ts @@ -412,6 +412,149 @@ export const generateSyncScopedName = memoize( }, ) as (localName: string, filePath: string, pattern: string, hashLength?: number) => string; +function parseVisitors( + options: ParserSyncOptions | ParserOptions, + valuesHandlers: Map>>, + preValuesHandlers: Map>>, + postValuesHandlers: Map>>, + errors: ErrorDescription[], + visitorsHandlersMap: Map< + "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", + Array | Record>> + >, + preVisitorsHandlersMap: Map< + "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", + Array | Record>>> + >, + postVisitorsHandlersMap: Map< + "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", + Array | Record>>> + >, +) { + const visitors = Object.entries(options.visitor!); + let key: string; + let value: any; + let i: number; + + for (i = 0; i < visitors.length; i++) { + key = visitors[i][0]; + value = visitors[i][1]; + + if (Number.isInteger(+key)) { + if (Array.isArray(value)) { + visitors.splice(i + 1, 0, ...Object.entries(value)); + continue; + } + + if (typeof value == "function") { + key = value.name; + } + } + + if (Array.isArray(value)) { + // @ts-ignore + visitors.splice(i + 1, 0, ...value.map((item) => [key, item])); + continue; + } + + if (key in EnumToken) { + if (typeof value == "function") { + if (!valuesHandlers.has(EnumToken[key as keyof typeof EnumToken] as EnumToken)) { + valuesHandlers.set(EnumToken[key as keyof typeof EnumToken] as EnumToken, []); + } + + valuesHandlers.get(EnumToken[key as keyof typeof EnumToken] as EnumToken)!.push(value); + } else if (typeof value == "object" && "type" in value && "handler" in value && value.type in WalkerEvent) { + if (value.type == WalkerEvent.Enter) { + if (!preValuesHandlers.has(EnumToken[key as keyof typeof EnumToken] as EnumToken)) { + preValuesHandlers.set(EnumToken[key as keyof typeof EnumToken] as EnumToken, []); + } + + preValuesHandlers.get(EnumToken[key as keyof typeof EnumToken] as EnumToken)!.push(value.handler); + } else if (value.type == WalkerEvent.Leave) { + if (!postValuesHandlers.has(EnumToken[key as keyof typeof EnumToken] as EnumToken)) { + postValuesHandlers.set(EnumToken[key as keyof typeof EnumToken] as EnumToken, []); + } + + postValuesHandlers.get(EnumToken[key as keyof typeof EnumToken] as EnumToken)!.push(value.handler); + } + } else { + errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); + } + } else if (["Declaration", "Rule", "AtRule", "KeyframesRule", "KeyframesAtRule"].includes(key)) { + if (typeof value == "function") { + if ( + !visitorsHandlersMap.has( + key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", + ) + ) { + visitorsHandlersMap.set( + key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", + [], + ); + } + + visitorsHandlersMap + .get(key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule")! + .push(value); + } else if (typeof value == "object") { + if ("type" in value && "handler" in value && value.type in WalkerEvent) { + if (value.type == WalkerEvent.Enter) { + if ( + !preVisitorsHandlersMap.has( + key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", + ) + ) { + preVisitorsHandlersMap.set( + key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", + [], + ); + } + + preVisitorsHandlersMap + .get(key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule")! + .push(value.handler); + } else if (value.type == WalkerEvent.Leave) { + if ( + !postVisitorsHandlersMap.has( + key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", + ) + ) { + postVisitorsHandlersMap.set( + key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", + [], + ); + } + + postVisitorsHandlersMap + .get(key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule")! + .push(value.handler); + } + } else { + if ( + !visitorsHandlersMap.has( + key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", + ) + ) { + visitorsHandlersMap.set( + key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", + [], + ); + } + + visitorsHandlersMap + .get(key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule")! + .push(value); + } + } else { + errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); + } + } else { + errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); + } + } +} + /** * Parse css string * @param iter @@ -520,151 +663,16 @@ export function doParseSync( let parensMatch: number = 0; let curlyBracketMatch: number = 0; - if (options.visitor != null) { - valuesHandlers = new Map() as Map>>; - preValuesHandlers = new Map() as Map>>; - postValuesHandlers = new Map() as Map>>; - - preVisitorsHandlersMap = new Map(); - visitorsHandlersMap = new Map(); - postVisitorsHandlersMap = new Map(); - - const visitors = Object.entries(options.visitor); - let key: string; - let value: any; - let i: number; - - for (i = 0; i < visitors.length; i++) { - key = visitors[i][0]; - value = visitors[i][1]; - - if (Number.isInteger(+key)) { - visitors.splice(i + 1, 0, ...Object.entries(value)); - continue; - } - - if (Array.isArray(value)) { - // @ts-ignore - visitors.splice(i + 1, 0, ...value.map((item) => [key, item])); - continue; - } - - if (key in EnumToken) { - if (typeof value == "function") { - if (!valuesHandlers.has(EnumToken[key as keyof typeof EnumToken] as EnumToken)) { - valuesHandlers.set(EnumToken[key as keyof typeof EnumToken] as EnumToken, []); - } - - valuesHandlers.get(EnumToken[key as keyof typeof EnumToken] as EnumToken)!.push(value); - } else if ( - typeof value == "object" && - "type" in value && - "handler" in value && - value.type in WalkerEvent - ) { - if (value.type == WalkerEvent.Enter) { - if (!preValuesHandlers.has(EnumToken[key as keyof typeof EnumToken] as EnumToken)) { - preValuesHandlers.set(EnumToken[key as keyof typeof EnumToken] as EnumToken, []); - } - - preValuesHandlers - .get(EnumToken[key as keyof typeof EnumToken] as EnumToken)! - .push(value.handler); - } else if (value.type == WalkerEvent.Leave) { - if (!postValuesHandlers.has(EnumToken[key as keyof typeof EnumToken] as EnumToken)) { - postValuesHandlers.set(EnumToken[key as keyof typeof EnumToken] as EnumToken, []); - } - - postValuesHandlers - .get(EnumToken[key as keyof typeof EnumToken] as EnumToken)! - .push(value.handler); - } - } else { - errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); - } - } else if (["Declaration", "Rule", "AtRule", "KeyframesRule", "KeyframesAtRule"].includes(key)) { - if (typeof value == "function") { - if ( - !visitorsHandlersMap.has( - key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", - ) - ) { - visitorsHandlersMap.set( - key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", - [], - ); - } - - visitorsHandlersMap - .get(key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule")! - .push(value); - } else if (typeof value == "object") { - if ("type" in value && "handler" in value && value.type in WalkerEvent) { - if (value.type == WalkerEvent.Enter) { - if ( - !preVisitorsHandlersMap.has( - key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", - ) - ) { - preVisitorsHandlersMap.set( - key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", - [], - ); - } - - preVisitorsHandlersMap - .get(key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule")! - .push(value.handler); - } else if (value.type == WalkerEvent.Leave) { - if ( - !postVisitorsHandlersMap.has( - key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", - ) - ) { - postVisitorsHandlersMap.set( - key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", - [], - ); - } - - postVisitorsHandlersMap - .get(key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule")! - .push(value.handler); - } - } else { - if ( - !visitorsHandlersMap.has( - key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", - ) - ) { - visitorsHandlersMap.set( - key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", - [], - ); - } - - visitorsHandlersMap - .get(key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule")! - .push(value); - } - } else { - errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); - } - } else { - errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); - } - } - } + let currentItemIndex: number; - if (Array.isArray(iter)) { - // @ts-expect-error - iter = iter[Symbol.iterator]() as Iterator; - } + // if (Array.isArray(iter)) { + // // @ts-expect-error + // iter = iter[Symbol.iterator]() as Iterator; + // } - while ( - // @ts-expect-error - (item = (iter as Iterator).next().value as TokenizeResult) + for (currentItemIndex = 0; currentItemIndex < (iter as Array).length; currentItemIndex++ ) { + item = (iter as Array)[currentItemIndex]; stats.bytesIn = item.bytesIn; stats.tokensCount++; @@ -696,9 +704,6 @@ export function doParseSync( tokens.push(item.token); - // console.debug([item.token, {parensMatch, curlyBracketMatch}]); - - // if (parensMatch === 0) { if ( parensMatch === 0 && (item.token.typ === EnumToken.SemiColonTokenType || @@ -717,8 +722,7 @@ export function doParseSync( tokens = [item.token]; do { - // @ts-expect-error - item = (iter as Iterator).next().value as TokenizeResult; + item = (iter as Array)[++currentItemIndex]; if (item == null) { break; @@ -787,278 +791,248 @@ export function doParseSync( } let replacement: GenericVisitorResult; - let callable: GenericVisitorHandler; if (options.visitor != null) { - let parens: Token[] | null; - for (const result of walk(ast)) { - parens = null; + valuesHandlers = new Map() as Map>>; + preValuesHandlers = new Map() as Map>>; + postValuesHandlers = new Map() as Map>>; - if ( - valuesHandlers!.size > 0 || - preVisitorsHandlersMap!.size > 0 || - visitorsHandlersMap!.size > 0 || - postVisitorsHandlersMap!.size > 0 - ) { - if ( - (result.node.typ == EnumToken.DeclarationNodeType && - (preVisitorsHandlersMap!.has("Declaration") || - visitorsHandlersMap!.has("Declaration") || - postVisitorsHandlersMap!.has("Declaration"))) || - (result.node.typ == EnumToken.AtRuleNodeType && - (preVisitorsHandlersMap!.has("AtRule") || - visitorsHandlersMap!.has("AtRule") || - postVisitorsHandlersMap!.has("AtRule"))) || - (result.node.typ == EnumToken.KeyframesAtRuleNodeType && - (preVisitorsHandlersMap!.has("KeyframesAtRule") || - visitorsHandlersMap!.has("KeyframesAtRule") || - postVisitorsHandlersMap!.has("KeyframesAtRule"))) - ) { - const handlers = [] as Array | Record>>; - const key = - result.node.typ == EnumToken.DeclarationNodeType - ? "Declaration" - : result.node.typ == EnumToken.AtRuleNodeType - ? "AtRule" - : "KeyframesAtRule"; - - if (preVisitorsHandlersMap!.has(key)) { - handlers.push( - // @ts-expect-error - ...(preVisitorsHandlersMap!.get(key)! as - | GenericVisitorHandler - | Record>), - ); - } + preVisitorsHandlersMap = new Map(); + visitorsHandlersMap = new Map(); + postVisitorsHandlersMap = new Map(); + parseVisitors( + options, + valuesHandlers, + preValuesHandlers, + postValuesHandlers, + errors, + visitorsHandlersMap, + preVisitorsHandlersMap, + postVisitorsHandlersMap, + ); - if (visitorsHandlersMap!.has(key)) { - // @ts-ignore - handlers.push(...visitorsHandlersMap.get(key)!); - } + let parens: Token[] | null; - if (postVisitorsHandlersMap!.has(key)) { - // @ts-ignore - handlers.push(...postVisitorsHandlersMap.get(key)); - } + let genericKey: string | null; + const handlers = [] as Array>; + const allHandlers = [] as Array< + | Map< + "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", + Array | Record>>> + > + | Map< + "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", + Array | Record>> + > + | Map>> + | Map< + EnumToken, + Array | Record>>> + > + >; + + if (preVisitorsHandlersMap!.size > 0) { + allHandlers.push(preVisitorsHandlersMap!); + } - let node: AstDeclaration | AstAtRule | AstKeyframesAtRule = result.node as - | AstDeclaration - | AstAtRule - | AstKeyframesAtRule; - - for (const handler of handlers) { - callable = - typeof handler == "function" - ? handler - : (handler[ - camelize( - node.typ === EnumToken.DeclarationNodeType || - node.typ === EnumToken.AtRuleNodeType - ? (node as AstDeclaration | AstAtRule).nam - : (node as AstKeyframesAtRule).val, - ) - ] as GenericVisitorHandler); - - if (callable == null) { - continue; - } + if (preValuesHandlers!.size > 0) { + allHandlers.push(preValuesHandlers!); + } - // @ts-expect-error - replacement = callable(node, result[PARENT], ast, function* () { - if (parens == null) { - // @ts-expect-error - parens = [...result.parents()]; - } + if (visitorsHandlersMap!.size > 0) { + allHandlers.push(visitorsHandlersMap!); + } - yield* parens[Symbol.iterator](); - }); + if (valuesHandlers!.size > 0) { + allHandlers.push(valuesHandlers!); + } - if (replacement == null) { - continue; - } + if (postVisitorsHandlersMap!.size > 0) { + allHandlers.push(postVisitorsHandlersMap!); + } - if (replacement == node) { - continue; - } + if (postValuesHandlers!.size > 0) { + allHandlers.push(postValuesHandlers!); + } - // @ts-ignore - node = replacement; + let nodes: AstNode[] | null = new Array(stats.tokensCount); + const subNodes: Array = []; + let i: number; + let k: number; + let j: number; + let freeBlock: number = 1; + const includeTokens: boolean = + preValuesHandlers!.size > 0 || valuesHandlers!.size > 0 || postValuesHandlers!.size > 0; - if (Array.isArray(node)) { - break; - } - } + nodes[0] = ast; - if (node != result.node) { - replaceNodeOrValue( - result.parent as - | AstRule - | AstAtRule - | AstKeyframesAtRule - | AstKeyframesRule - | AstStyleSheet, - result.node, - node, - ); - } - } else if ( - (result.node.typ == EnumToken.RuleNodeType && - (preVisitorsHandlersMap!.has("Rule") || - visitorsHandlersMap!.has("Rule") || - postVisitorsHandlersMap!.has("Rule"))) || - (result.node.typ == EnumToken.KeyFramesRuleNodeType && - (preVisitorsHandlersMap!.has("KeyframesRule") || - visitorsHandlersMap!.has("KeyframesRule") || - postVisitorsHandlersMap!.has("KeyframesRule"))) - ) { - const handlers = [] as Array< - | GenericVisitorHandler - | { - type: WalkerEvent; - handler: GenericVisitorHandler; - } - >; - const key = result.node.typ == EnumToken.RuleNodeType ? "Rule" : "KeyframesRule"; - - if (preVisitorsHandlersMap!.has(key)) { - handlers.push(...(preVisitorsHandlersMap!.get(key)! as Array>)); - } + for (i = 0; i < nodes.length; i++) { + if (nodes[i] == null) { + break; + } - if (visitorsHandlersMap!.has(key)) { - handlers.push(...(visitorsHandlersMap!.get(key)! as Array>)); - } + subNodes.length = 0; + if (includeTokens) { + switch (nodes[i].typ) { + case EnumToken.RuleNodeType: + case EnumToken.AtRuleNodeType: + case EnumToken.KeyframesRuleNodeType: + case EnumToken.KeyframesAtRuleNodeType: + subNodes.push( + ...(nodes[i] as AstRule | AstAtRule | AstKeyframesRule | AstKeyframesAtRule)[TOKENS]!, + ); + break; + case EnumToken.DeclarationNodeType: + subNodes.push(...(nodes[i] as AstDeclaration).val); + break; + } + } - if (postVisitorsHandlersMap!.has(key)) { - handlers.push(...(postVisitorsHandlersMap!.get(key)! as Array>)); - } + if (nodes[i].chi != null) { + subNodes.push(...nodes[i].chi); + } - let node = result.node; + if (subNodes.length > 0) { + if (freeBlock <= i) { + freeBlock = i + 1; + } - for (const callable of handlers) { - replacement = (callable as GenericVisitorHandler)( - node as T, - result.parent, - result.root, - // @ts-expect-error - function* () { - if (parens == null) { - // @ts-expect-error - parens = [...result.parents()]; - } + for (k = 0; k < subNodes.length; k++) { + j = k + freeBlock; + nodes[j] = subNodes[k] as AstNode; + nodes[j][PARENT] = nodes[i]; + } - yield* parens[Symbol.iterator](); - }, - ) as GenericVisitorResult; + freeBlock += subNodes.length; + } - if (replacement == null) { - continue; - } + parens = null; + handlers.length = 0; + + genericKey = + nodes[i].typ == EnumToken.DeclarationNodeType + ? "Declaration" + : nodes[i].typ == EnumToken.AtRuleNodeType + ? "AtRule" + : nodes[i].typ == EnumToken.KeyframesAtRuleNodeType + ? "KeyframesAtRule" + : nodes[i].typ === EnumToken.KeyframesRuleNodeType + ? "KeyframesRule" + : nodes[i].typ == EnumToken.RuleNodeType + ? "Rule" + : nodes[i].typ == EnumToken.KeyframesRuleNodeType + ? "KeyframesRule" + : null; + let keyName: string | null = + nodes[i].typ == EnumToken.DeclarationNodeType || nodes[i].typ == EnumToken.AtRuleNodeType + ? camelize((nodes[i] as AstDeclaration | AstAtRule).nam) + : nodes[i].typ == EnumToken.KeyframesAtRuleNodeType + ? camelize((nodes[i] as AstKeyframesAtRule).val) + : null; + + for (const map of allHandlers) { + // @ts-ignore + if (genericKey != null && map!.has(genericKey)) { + // @ts-ignore + for (const handler of map!.get(genericKey)!) { + if (typeof handler == "function") { + handlers.push(handler as GenericVisitorHandler); + } else if (Array.isArray(handler)) { + for (const h of handler) { + if (typeof h == "function") { + handlers.push(h); + } - if (replacement == node) { - continue; + // @ts-ignore + else if (h[keyName] != null) { + // @ts-ignore + handlers.push(h[keyName]); + } + } + } else if (typeof handler.handler! == "function") { + handlers.push(handler.handler); } // @ts-ignore - node = replacement as AstNode; - - // - if (Array.isArray(node)) { - break; + else if (typeof handler[keyName]! == "function") { + // @ts-ignore + handlers.push(handler[keyName]); } } + } + // @ts-ignore + if (map!.has(nodes[i].typ)) { // @ts-ignore - if (node != result.node) { - // @ts-ignore - replaceNodeOrValue(result.parent, result.node, node); - } - } else if (valuesHandlers!.size > 0) { - let node: Token | AstNode | null = null; - - node = result.node; - - if (valuesHandlers!.has(node.typ)) { - for (const valueHandler of valuesHandlers!.get(node.typ)!) { - callable = valueHandler as GenericVisitorHandler; - replacement = callable( - node as T, - result.parent, - ast, - // @ts-expect-error - function* () { - if (parens == null) { - // @ts-expect-error - parens = [...result.parents()]; - } - - yield* parens[Symbol.iterator](); - }, - ); - - if (replacement == null) { - continue; - } - - if (replacement != node) { - node = replacement as AstNode; + for (const handler of map!.get(nodes[i].typ)!) { + if (typeof handler == "function") { + handlers.push(handler as GenericVisitorHandler); + } else if (Array.isArray(handler)) { + for (const h of handler) { + if (typeof h == "function") { + handlers.push(h); + } } + } else if (typeof handler.handler! == "function") { + handlers.push(handler.handler); } - } - if (node != result.node) { // @ts-ignore - replaceNodeOrValue(result[PARENT], value, node); - } - - const tokens: Token[] = Array.isArray(result.node[TOKENS]) ? (result.node[TOKENS] as Token[]) : []; - - if (Array.isArray(result.node.val)) { - tokens.push(...(result.node.val as Token[])); + else if (typeof handler[keyName]! == "function") { + // @ts-ignore + handlers.push(handler[keyName]); + } } + } + } - if (tokens.length == 0) { - continue; - } + if (handlers.length == 0) { + continue; + } - for (const { value, parent, root, parents } of walkValues(tokens, result.node)) { - node = value; + let node = nodes[i]; - if (valuesHandlers!.has(node!.typ)) { - let parens: Token[] | null = null; - for (const valueHandler of valuesHandlers!.get(node!.typ)!) { - callable = valueHandler as GenericVisitorHandler; - // @ts-expect-error - let result: GenericVisitorResult = callable(node as T, parent, root, function* () { - if (parens == null) { - // @ts-expect-error - parens = [...parents()]; - } + for (const callable of handlers) { + replacement = (callable as GenericVisitorHandler)( + node as T, + nodes[i][PARENT] as AstNode, + ast as AstStyleSheet, + // @ts-expect-error + function* () { + if (parens == null) { + let node = nodes![i][PARENT] as AstNode; - yield* parens[Symbol.iterator](); - }); + while (node != null) { + yield node; + node = node[PARENT] as AstNode; + } + } + }, + ) as GenericVisitorResult; - if (result == null) { - continue; - } + if (replacement == null) { + continue; + } - if (result != node) { - node = result as Token; - } + if (replacement == node) { + continue; + } - if (Array.isArray(node)) { - break; - } - } - } + // @ts-ignore + node = replacement as AstNode; - if (node != value) { - // @ts-ignore - replaceNodeOrValue(parent, value, node); - } - } + // + if (Array.isArray(node)) { + break; } } + + if (node != nodes[i]) { + replaceNodeOrValue(nodes[i][PARENT], nodes[i], node); + } } + + nodes = null; } if (invalidNodes.length > 0) { @@ -1138,7 +1112,7 @@ export function doParseSync( scoped: ModuleScopeEnumOptions.Local, naming: ModuleCaseTransformEnum.IgnoreCase, pattern: "", - generateScopedName, + generateScopedName: generateSyncScopedName, ...(typeof options.module != "object" ? {} : options.module), } as ModuleSyncOptions; @@ -1876,142 +1850,6 @@ export async function doParse( let parensMatch: number = 0; let curlyBracketMatch: number = 0; - if (options.visitor != null) { - valuesHandlers = new Map() as Map>>; - preValuesHandlers = new Map() as Map>>; - postValuesHandlers = new Map() as Map>>; - - preVisitorsHandlersMap = new Map(); - visitorsHandlersMap = new Map(); - postVisitorsHandlersMap = new Map(); - - const visitors = Object.entries(options.visitor); - let key: string; - let value: any; - let i: number; - - for (i = 0; i < visitors.length; i++) { - key = visitors[i][0]; - value = visitors[i][1]; - - if (Number.isInteger(+key)) { - visitors.splice(i + 1, 0, ...Object.entries(value)); - continue; - } - - if (Array.isArray(value)) { - // @ts-ignore - visitors.splice(i + 1, 0, ...value.map((item) => [key, item])); - continue; - } - - if (key in EnumToken) { - if (typeof value == "function") { - if (!valuesHandlers.has(EnumToken[key as keyof typeof EnumToken] as EnumToken)) { - valuesHandlers.set(EnumToken[key as keyof typeof EnumToken] as EnumToken, []); - } - - valuesHandlers.get(EnumToken[key as keyof typeof EnumToken] as EnumToken)!.push(value); - } else if ( - typeof value == "object" && - "type" in value && - "handler" in value && - value.type in WalkerEvent - ) { - if (value.type == WalkerEvent.Enter) { - if (!preValuesHandlers.has(EnumToken[key as keyof typeof EnumToken] as EnumToken)) { - preValuesHandlers.set(EnumToken[key as keyof typeof EnumToken] as EnumToken, []); - } - - preValuesHandlers - .get(EnumToken[key as keyof typeof EnumToken] as EnumToken)! - .push(value.handler); - } else if (value.type == WalkerEvent.Leave) { - if (!postValuesHandlers.has(EnumToken[key as keyof typeof EnumToken] as EnumToken)) { - postValuesHandlers.set(EnumToken[key as keyof typeof EnumToken] as EnumToken, []); - } - - postValuesHandlers - .get(EnumToken[key as keyof typeof EnumToken] as EnumToken)! - .push(value.handler); - } - } else { - errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); - } - } else if (["Declaration", "Rule", "AtRule", "KeyframesRule", "KeyframesAtRule"].includes(key)) { - if (typeof value == "function") { - if ( - !visitorsHandlersMap.has( - key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", - ) - ) { - visitorsHandlersMap.set( - key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", - [], - ); - } - - visitorsHandlersMap - .get(key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule")! - .push(value); - } else if (typeof value == "object") { - if ("type" in value && "handler" in value && value.type in WalkerEvent) { - if (value.type == WalkerEvent.Enter) { - if ( - !preVisitorsHandlersMap.has( - key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", - ) - ) { - preVisitorsHandlersMap.set( - key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", - [], - ); - } - - preVisitorsHandlersMap - .get(key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule")! - .push(value.handler); - } else if (value.type == WalkerEvent.Leave) { - if ( - !postVisitorsHandlersMap.has( - key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", - ) - ) { - postVisitorsHandlersMap.set( - key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", - [], - ); - } - - postVisitorsHandlersMap - .get(key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule")! - .push(value.handler); - } - } else { - if ( - !visitorsHandlersMap.has( - key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", - ) - ) { - visitorsHandlersMap.set( - key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", - [], - ); - } - - visitorsHandlersMap - .get(key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule")! - .push(value); - } - } else { - errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); - } - } else { - errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); - } - } - } - if (Array.isArray(iter)) { // @ts-expect-error iter = iter[Symbol.iterator]() as Iterator; @@ -2212,7 +2050,6 @@ export async function doParse( } let replacement: GenericVisitorResult; - let callable: GenericVisitorHandler; while (stack.length > 0 && context != ast) { const previousNode: AstAtRule | AstRule = stack.pop() as AstAtRule | AstRule; @@ -2235,291 +2072,251 @@ export async function doParse( } if (options.visitor != null) { - let parens: Token[] | null; - for (const result of walk(ast)) { - parens = null; - - if ( - valuesHandlers!.size > 0 || - preVisitorsHandlersMap!.size > 0 || - visitorsHandlersMap!.size > 0 || - postVisitorsHandlersMap!.size > 0 - ) { - if ( - (result.node.typ == EnumToken.DeclarationNodeType && - (preVisitorsHandlersMap!.has("Declaration") || - visitorsHandlersMap!.has("Declaration") || - postVisitorsHandlersMap!.has("Declaration"))) || - (result.node.typ == EnumToken.AtRuleNodeType && - (preVisitorsHandlersMap!.has("AtRule") || - visitorsHandlersMap!.has("AtRule") || - postVisitorsHandlersMap!.has("AtRule"))) || - (result.node.typ == EnumToken.KeyframesAtRuleNodeType && - (preVisitorsHandlersMap!.has("KeyframesAtRule") || - visitorsHandlersMap!.has("KeyframesAtRule") || - postVisitorsHandlersMap!.has("KeyframesAtRule"))) - ) { - const handlers = [] as Array | Record>>; - const key = - result.node.typ == EnumToken.DeclarationNodeType - ? "Declaration" - : result.node.typ == EnumToken.AtRuleNodeType - ? "AtRule" - : "KeyframesAtRule"; - - if (preVisitorsHandlersMap!.has(key)) { - handlers.push( - // @ts-expect-error - ...(preVisitorsHandlersMap!.get(key)! as - | GenericVisitorHandler - | Record>), - ); - } + valuesHandlers = new Map() as Map>>; + preValuesHandlers = new Map() as Map>>; + postValuesHandlers = new Map() as Map>>; - if (visitorsHandlersMap!.has(key)) { - // @ts-ignore - handlers.push(...visitorsHandlersMap.get(key)!); - } + preVisitorsHandlersMap = new Map(); + visitorsHandlersMap = new Map(); + postVisitorsHandlersMap = new Map(); - if (postVisitorsHandlersMap!.has(key)) { - // @ts-ignore - handlers.push(...postVisitorsHandlersMap.get(key)); - } + parseVisitors( + options as ParserSyncOptions, + valuesHandlers, + preValuesHandlers, + postValuesHandlers, + errors, + visitorsHandlersMap, + preVisitorsHandlersMap, + postVisitorsHandlersMap, + ); - let node: AstDeclaration | AstAtRule | AstKeyframesAtRule = result.node as - | AstDeclaration - | AstAtRule - | AstKeyframesAtRule; - - for (const handler of handlers) { - callable = - typeof handler == "function" - ? handler - : (handler[ - camelize( - node.typ === EnumToken.DeclarationNodeType || - node.typ === EnumToken.AtRuleNodeType - ? (node as AstDeclaration | AstAtRule).nam - : (node as AstKeyframesAtRule).val, - ) - ] as GenericVisitorHandler); - - if (callable == null) { - continue; - } + let parens: Token[] | null; - // @ts-expect-error - replacement = callable(node, result[PARENT], ast, function* () { - if (parens == null) { - // @ts-expect-error - parens = [...result.parents()]; - } + let genericKey: string | null; + const handlers = [] as Array>; + const allHandlers = [] as Array< + | Map< + "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", + Array | Record>>> + > + | Map< + "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", + Array | Record>> + > + | Map>> + | Map< + EnumToken, + Array | Record>>> + > + >; + + if (preVisitorsHandlersMap!.size > 0) { + allHandlers.push(preVisitorsHandlersMap!); + } - yield* parens[Symbol.iterator](); - }); + if (preValuesHandlers!.size > 0) { + allHandlers.push(preValuesHandlers!); + } - if (replacement == null) { - continue; - } + if (visitorsHandlersMap!.size > 0) { + allHandlers.push(visitorsHandlersMap!); + } - if (replacement instanceof Promise) { - replacement = await replacement; - } + if (valuesHandlers!.size > 0) { + allHandlers.push(valuesHandlers!); + } - if (replacement == null || replacement == node) { - continue; - } + if (postVisitorsHandlersMap!.size > 0) { + allHandlers.push(postVisitorsHandlersMap!); + } - // @ts-ignore - node = replacement; + if (postValuesHandlers!.size > 0) { + allHandlers.push(postValuesHandlers!); + } - if (Array.isArray(node)) { - break; - } - } + let nodes: AstNode[] | null = new Array(stats.tokensCount); + const subNodes: Array = []; + let i: number; + let k: number; + let j: number; + let freeblock: number = 1; + const includeTokens: boolean = + preValuesHandlers!.size > 0 || valuesHandlers!.size > 0 || postValuesHandlers!.size > 0; - if (node != result.node) { - replaceNodeOrValue( - result.parent as - | AstRule - | AstAtRule - | AstKeyframesAtRule - | AstKeyframesRule - | AstStyleSheet, - result.node, - node, - ); - } - } else if ( - (result.node.typ == EnumToken.RuleNodeType && - (preVisitorsHandlersMap!.has("Rule") || - visitorsHandlersMap!.has("Rule") || - postVisitorsHandlersMap!.has("Rule"))) || - (result.node.typ == EnumToken.KeyFramesRuleNodeType && - (preVisitorsHandlersMap!.has("KeyframesRule") || - visitorsHandlersMap!.has("KeyframesRule") || - postVisitorsHandlersMap!.has("KeyframesRule"))) - ) { - const handlers = [] as Array< - | GenericVisitorHandler - | { - type: WalkerEvent; - handler: GenericVisitorHandler; - } - >; - const key = result.node.typ == EnumToken.RuleNodeType ? "Rule" : "KeyframesRule"; - - if (preVisitorsHandlersMap!.has(key)) { - handlers.push(...(preVisitorsHandlersMap!.get(key)! as Array>)); - } + nodes[0] = ast; - if (visitorsHandlersMap!.has(key)) { - handlers.push(...(visitorsHandlersMap!.get(key)! as Array>)); - } + for (i = 0; i < nodes.length; i++) { + if (nodes[i] == null) { + break; + } - if (postVisitorsHandlersMap!.has(key)) { - handlers.push(...(postVisitorsHandlersMap!.get(key)! as Array>)); - } + subNodes.length = 0; + if (includeTokens) { + switch (nodes[i].typ) { + case EnumToken.RuleNodeType: + case EnumToken.AtRuleNodeType: + case EnumToken.KeyframesRuleNodeType: + case EnumToken.KeyframesAtRuleNodeType: + subNodes.push( + ...(nodes[i] as AstRule | AstAtRule | AstKeyframesRule | AstKeyframesAtRule)[TOKENS]!, + ); + break; + case EnumToken.DeclarationNodeType: + subNodes.push(...(nodes[i] as AstDeclaration).val); + break; + } + } - let node = result.node; + if (nodes[i].chi != null) { + subNodes.push(...nodes[i].chi); + } - for (const callable of handlers) { - replacement = (callable as GenericVisitorHandler)( - node as T, - result.parent, - result.root, - // @ts-expect-error - function* () { - if (parens == null) { - // @ts-expect-error - parens = [...result.parents()]; - } + if (subNodes.length > 0) { + if (freeblock <= i) { + freeblock = i + 1; + } - yield* parens[Symbol.iterator](); - }, - ) as GenericVisitorResult; + for (k = 0; k < subNodes.length; k++) { + j = k + freeblock; + nodes[j] = subNodes[k] as AstNode; + nodes[j][PARENT] = nodes[i]; + } - if (replacement == null) { - continue; - } + freeblock += subNodes.length; + } - if (replacement instanceof Promise) { - replacement = await replacement; - } + parens = null; + handlers.length = 0; + + genericKey = + nodes[i].typ == EnumToken.DeclarationNodeType + ? "Declaration" + : nodes[i].typ == EnumToken.AtRuleNodeType + ? "AtRule" + : nodes[i].typ == EnumToken.KeyframesAtRuleNodeType + ? "KeyframesAtRule" + : nodes[i].typ === EnumToken.KeyframesRuleNodeType + ? "KeyframesRule" + : nodes[i].typ == EnumToken.RuleNodeType + ? "Rule" + : nodes[i].typ == EnumToken.KeyframesRuleNodeType + ? "KeyframesRule" + : null; + let keyName: string | null = + nodes[i].typ == EnumToken.DeclarationNodeType || nodes[i].typ == EnumToken.AtRuleNodeType + ? camelize((nodes[i] as AstDeclaration | AstAtRule).nam) + : nodes[i].typ == EnumToken.KeyframesAtRuleNodeType + ? camelize((nodes[i] as AstKeyframesAtRule).val) + : null; + + for (const map of allHandlers) { + // @ts-ignore + if (genericKey != null && map!.has(genericKey)) { + // @ts-ignore + for (const handler of map!.get(genericKey)!) { + if (typeof handler == "function") { + handlers.push(handler as GenericVisitorHandler); + } else if (Array.isArray(handler)) { + for (const h of handler) { + if (typeof h == "function") { + handlers.push(h); + } - if (replacement == null || replacement == node) { - continue; + // @ts-ignore + else if (h[keyName] != null) { + // @ts-ignore + handlers.push(h[keyName]); + } + } + } else if (typeof handler.handler! == "function") { + handlers.push(handler.handler); } // @ts-ignore - node = replacement as AstNode; - - // - if (Array.isArray(node)) { - break; + else if (typeof handler[keyName]! == "function") { + // @ts-ignore + handlers.push(handler[keyName]); } } + } + // @ts-ignore + if (map!.has(nodes[i].typ)) { // @ts-ignore - if (node != result.node) { - // @ts-ignore - replaceNodeOrValue(result.parent, result.node, node); - } - } else if (valuesHandlers!.size > 0) { - let node: Token | AstNode | null = null; - - node = result.node; - - if (valuesHandlers!.has(node.typ)) { - for (const valueHandler of valuesHandlers!.get(node.typ)!) { - callable = valueHandler as GenericVisitorHandler; - replacement = callable( - node as T, - result.parent, - ast, - // @ts-expect-error - function* () { - if (parens == null) { - // @ts-expect-error - parens = [...result.parents()]; - } - - yield* parens[Symbol.iterator](); - }, - ); - - if (replacement == null) { - continue; - } - - if (replacement instanceof Promise) { - replacement = await replacement; - } - - if (replacement != null && replacement != node) { - node = replacement as AstNode; + for (const handler of map!.get(nodes[i].typ)!) { + if (typeof handler == "function") { + handlers.push(handler as GenericVisitorHandler); + } else if (Array.isArray(handler)) { + for (const h of handler) { + if (typeof h == "function") { + handlers.push(h); + } } + } else if (typeof handler.handler! == "function") { + handlers.push(handler.handler); } - } - if (node != result.node) { // @ts-ignore - replaceNodeOrValue(result[PARENT], value, node); - } - - const tokens: Token[] = Array.isArray(result.node[TOKENS]) ? (result.node[TOKENS] as Token[]) : []; - - if (Array.isArray(result.node.val)) { - tokens.push(...(result.node.val as Token[])); + else if (typeof handler[keyName]! == "function") { + // @ts-ignore + handlers.push(handler[keyName]); + } } + } + } - if (tokens.length == 0) { - continue; - } + if (handlers.length == 0) { + continue; + } - for (const { value, parent, root, parents } of walkValues(tokens, result.node)) { - node = value; + let node = nodes[i] as AstNode; - if (valuesHandlers!.has(node!.typ)) { - let parens: Token[] | null = null; - for (const valueHandler of valuesHandlers!.get(node!.typ)!) { - callable = valueHandler as GenericVisitorHandler; - // @ts-expect-error - let result: GenericVisitorResult = callable(node as T, parent, root, function* () { - if (parens == null) { - // @ts-expect-error - parens = [...parents()]; - } + for (const callable of handlers) { + replacement = (callable as GenericVisitorHandler)( + node as T, + nodes[i][PARENT] as AstNode, + ast as AstStyleSheet, + // @ts-expect-error + function* () { + if (parens == null) { + let node = nodes![i][PARENT] as AstNode; - yield* parens[Symbol.iterator](); - }); + while (node != null) { + yield node; + node = node[PARENT] as AstNode; + } + } + }, + ) as GenericVisitorResult; - if (result == null) { - continue; - } + if (replacement == null) { + continue; + } - if (result instanceof Promise) { - result = await result; - } + if (replacement instanceof Promise) { + replacement = await replacement; + } - if (result != null && result != node) { - node = result as Token; - } + if (replacement == null || replacement == node) { + continue; + } - if (Array.isArray(node)) { - break; - } - } - } + // @ts-ignore + node = replacement as AstNode; - if (node != value) { - // @ts-ignore - replaceNodeOrValue(parent, value, node); - } - } + // + if (Array.isArray(node)) { + break; } } + + if (node != nodes[i]) { + replaceNodeOrValue(nodes[i][PARENT], nodes[i], node); + } } + + nodes = null; } if (invalidNodes.length > 0) { diff --git a/src/lib/parser/utils/selector.ts b/src/lib/parser/utils/selector.ts index 456a88c9..4dfa2340 100644 --- a/src/lib/parser/utils/selector.ts +++ b/src/lib/parser/utils/selector.ts @@ -97,7 +97,7 @@ export function parseSelector( ); return { - typ: EnumToken.KeyFramesRuleNodeType, + typ: EnumToken.KeyframesRuleNodeType, sel: [ ...splitTokenList(trimArray(tokens)).reduce((acc, curr: Token[]) => { acc.add(curr.reduce((acc, curr) => acc + renderValue(curr, { minify: false }), "")); diff --git a/src/lib/renderer/render.ts b/src/lib/renderer/render.ts index 0e2db5fb..ee0d6106 100644 --- a/src/lib/renderer/render.ts +++ b/src/lib/renderer/render.ts @@ -281,7 +281,7 @@ function updateSourceMap( [ EnumToken.RuleNodeType, EnumToken.AtRuleNodeType, - EnumToken.KeyFramesRuleNodeType, + EnumToken.KeyframesRuleNodeType, EnumToken.KeyframesAtRuleNodeType, ].includes(node.typ) ) { @@ -477,7 +477,7 @@ function renderAstNode( case EnumToken.AtRuleNodeType: case EnumToken.RuleNodeType: - case EnumToken.KeyFramesRuleNodeType: + case EnumToken.KeyframesRuleNodeType: case EnumToken.KeyframesAtRuleNodeType: if ([EnumToken.AtRuleNodeType, EnumToken.KeyframesAtRuleNodeType].includes(data.typ) && !("chi" in data)) { return `${indent}@${(data).nam}${(data).val === "" ? "" : options.indent || " "}${ diff --git a/test/specs/code/modules.js b/test/specs/code/modules.js index f73093b5..d038667e 100644 --- a/test/specs/code/modules.js +++ b/test/specs/code/modules.js @@ -1,6 +1,6 @@ import { ColorType, EnumToken, ModuleCaseTransformEnum, ModuleScopeEnumOptions } from "../../../dist/lib/ast/types.js"; -export function run(describe, expect, it, transform, parse, render, dirname, readFile) { +export function run(describe, expect, it, transform, parse, render, dirname, readFile, resolve, ColorType, EnumToken, ModuleCaseTransformEnum, ModuleScopeEnumOptions, transformSync, parseSync) { describe("css modules", function () { it("module #1", function () { return transform( @@ -930,5 +930,37 @@ a span { }`); }); }); + + it("module #24", function () { + const result = transformSync( + ` +.goal .bg-indigo { + background: indigo; +} + +.indigo-white { + composes: bg-indigo title; + color: white; +} +`, + { + module: true, + beautify: true, + }, + ); + + expect(result.mapping).deep.equals({ + goal: "goal_r7bhp", + "bg-indigo": "bg-indigo_gy28g", + "indigo-white": "indigo-white_wims0 bg-indigo_gy28g title_qw06e", + title: "title_qw06e", + }); + expect(result.code).equals(`.goal_r7bhp .bg-indigo_gy28g { + background: indigo +} +.indigo-white_wims0 { + color: #fff +}`); + }); }); } diff --git a/test/specs/code/visitors.js b/test/specs/code/visitors.js index 148c338a..bda89685 100644 --- a/test/specs/code/visitors.js +++ b/test/specs/code/visitors.js @@ -1,11 +1,25 @@ -import {ColorType, EnumToken} from "../../../dist/lib/ast/types.js"; - -export function run(describe, expect, it, transform, parse, render, dirname, readFile) { - - describe('node visitor', function () { - - it('visitor #1', function () { - +import { ColorType, EnumToken } from "../../../dist/lib/ast/types.js"; +import { WalkerEvent } from "../../../dist/lib/ast/walk.js"; + +export function run( + describe, + expect, + it, + transform, + parse, + render, + dirname, + readFile, + resolve, + ColorType, + EnumToken, + ModuleCaseTransformEnum, + ModuleScopeEnumOptions, + transformSync, + parseSync, +) { + describe("node visitor", function () { + it("visitor #1", function () { const css = ` @media screen { @@ -16,50 +30,43 @@ export function run(describe, expect, it, transform, parse, render, dirname, rea } `; const options = { - visitor: { - AtRule: { - media: (node) => { - - return {...node, val: 'all'} - } + return { ...node, val: "all" }; + }, }, Rule(node) { - - return {...node, sel: '.foo,.bar,.fubar'}; + return { ...node, sel: ".foo,.bar,.fubar" }; }, Declaration: { - height: (node) => { - return [ node, { - typ: EnumToken.DeclarationNodeType, - nam: 'width', + nam: "width", val: [ { typ: EnumToken.Length, - val: '3', - unit: 'px' - } - ] - } - ] - } - } - } - } + val: "3", + unit: "px", + }, + ], + }, + ]; + }, + }, + }, + }; - return transform(css, options).then(result => expect(result.code).equals('.foo,.bar,.fubar{height:calc(40px/3);width:3px}')); + return transform(css, options).then((result) => + expect(result.code).equals(".foo,.bar,.fubar{height:calc(40px/3);width:3px}"), + ); }); - it('visitor #2', function () { - + it("visitor #2", function () { const css = ` body { color: color(from var(--base-color) display-p3 r calc(g + 0.24) calc(b + 0.15)); } @@ -76,29 +83,25 @@ body { } `; const options = { - beautify: true, visitor: { - DeclarationNodeType: (declaration) => { - - if (declaration.nam == 'height') { - - declaration.nam = 'width'; + if (declaration.nam == "height") { + declaration.nam = "width"; } }, ColorTokenType: (color) => { - return { typ: EnumToken.Color, - val: 'red', - kin: ColorType.HEX - } - } - } - } + val: "red", + kin: ColorType.HEX, + }; + }, + }, + }; - return transform(css, options).then(result => expect(result.code).equals(`body { + return transform(css, options).then((result) => + expect(result.code).equals(`body { color: red } html,body { @@ -107,11 +110,11 @@ html,body { .ruler { width: 10px; background-color: red -}`)); +}`), + ); }); - it('visitor #3', function () { - + it("visitor #3", function () { const css = ` @media screen { @@ -136,37 +139,32 @@ body { } `; const options = { - beautify: true, inlineCssVariables: true, resolveImport: true, visitor: { - StyleSheetNodeType: async (node) => { - // insert a new rule - node.chi.unshift(await parse('html {--base-color: pink}').then(result => result.ast.chi[0])) + node.chi.unshift(await parse("html {--base-color: pink}").then((result) => result.ast.chi[0])); }, - ColorTokenType: (node) => { - + ColorTokenType: (node) => { // dump all color tokens // console.debug(node); }, - FunctionTokenType: (node) => { - + FunctionTokenType: (node) => { // dump all function tokens // console.debug(node); }, - DeclarationNodeType: (node) => { - + DeclarationNodeType: (node) => { // dump all declaration nodes // console.debug(node); - } - } + }, + }, }; - return transform(css, options).then(result => expect(result.code).equals(`@media screen { + return transform(css, options).then((result) => + expect(result.code).equals(`@media screen { .foo:-webkit-autofill { height: calc(40px/3) } @@ -180,11 +178,11 @@ html,body { .ruler { height: 10px; background-color: orange -}`)); +}`), + ); }); - it('visitor #4', function () { - + it("visitor #4", function () { const css = ` @keyframes slide-in { @@ -215,21 +213,20 @@ html,body { } `; const options = { - removePrefix: true, beautify: true, visitor: { KeyframesAtRule: { slideIn(node) { - - node.val = 'slide-in-out'; + node.val = "slide-in-out"; return node; - } - } - } - } + }, + }, + }, + }; - return transform(css, options).then(result => expect(result.code).equals(`@keyframes slide-in-out { + return transform(css, options).then((result) => + expect(result.code).equals(`@keyframes slide-in-out { 0% { transform: translateX(0) } @@ -252,9 +249,248 @@ html,body { top: 100px; left: 100% } -}`)); +}`), + ); }); - }); + it("visitor #5", function () { + const css = ` + +body { color: color(from var(--base-color) display-p3 r calc(g + 0.24) calc(b + 0.15)); } + +html, +body { + line-height: 1.474; +} + +.ruler { + + height: 10px; + background-color: orange +} +`; + const options = { + beautify: true, + visitor: { + DeclarationNodeType: { + type: WalkerEvent.Enter, + handler: (declaration) => { + if (declaration.nam == "height") { + declaration.nam = "width"; + } + }, + }, + ColorTokenType: (color) => { + return { + typ: EnumToken.Color, + val: "red", + kin: ColorType.HEX, + }; + }, + }, + }; + + const result = transformSync(css, options); + + expect(result.code).equals(`body { + color: red +} +html,body { + line-height: 1.474 +} +.ruler { + width: 10px; + background-color: red +}`); + }); + + it("visitor #6", function () { + const css = ` + +body { color: color(from var(--base-color) display-p3 r calc(g + 0.24) calc(b + 0.15)); } + +html, +body { + line-height: 1.474; +} + +.ruler { + + height: 10px; + background-color: orange +} +`; + const options = { + beautify: true, + visitor: { + DeclarationNodeType: (declaration) => { + if (declaration.nam == "height") { + declaration.nam = "width"; + } + }, + ColorTokenType: (color) => { + return { + typ: EnumToken.Color, + val: "red", + kin: ColorType.HEX, + }; + }, + }, + }; + + const result = transformSync(css, options); + + expect(result.code).equals(`body { + color: red +} +html,body { + line-height: 1.474 +} +.ruler { + width: 10px; + background-color: red +}`); + }); + + it("visitor #7", function () { + const css = ` + +@media screen { + + .foo:-webkit-autofill { + height: calc(100px * 2/ 15); + } +} + + +body { color: color(from var(--base-color) display-p3 r calc(g + 0.24) calc(b + 0.15)); } + +html, +body { + line-height: 1.474; +} + +.ruler { + + height: 10px; + background-color: orange +} +`; + const options = { + beautify: true, + inlineCssVariables: true, + resolveImport: true, + visitor: { + StyleSheetNodeType: { + type: WalkerEvent.Leave, + handler: (node) => { + // insert a new rule + node.chi.unshift(parseSync("html {--base-color: pink}").ast.chi[0]); + }, + }, + ColorTokenType: (node) => { + // dump all color tokens + // console.debug(node); + }, + FunctionTokenType: (node) => { + // dump all function tokens + // console.debug(node); + }, + DeclarationNodeType: (node) => { + // dump all declaration nodes + // console.debug(node); + }, + }, + }; + + const result = transformSync(css, options); + + expect(result.code).equals(`@media screen { + .foo:-webkit-autofill { + height: calc(40px/3) + } +} +body { + color: #f3fff0 +} +html,body { + line-height: 1.474 +} +.ruler { + height: 10px; + background-color: orange +}`); + }); + + it("visitor #8", function () { + const css = ` + +@keyframes slide-in { + from { + transform: translateX(0%); + } -} \ No newline at end of file + to { + transform: translateX(100%); + } +} +@keyframes identifier { + 0% { + top: 0; + left: 0; + } + 30% { + top: 50px; + } + 68%, + 72% { + left: 50px; + } + 100% { + top: 100px; + left: 100%; + } +} +`; + const options = { + removePrefix: true, + beautify: true, + visitor: { + KeyframesAtRule: { + slideIn(node) { + node.val = "slide-in-out"; + return node; + }, + }, + }, + }; + + const result = transformSync(css, options); + + expect(result.code).equals(`@keyframes slide-in-out { + 0% { + transform: translateX(0) + } + to { + transform: translateX(100%) + } +} +@keyframes identifier { + 0% { + top: 0; + left: 0 + } + 30% { + top: 50px + } + 68%,72% { + left: 50px + } + to { + top: 100px; + left: 100% + } +}`); + }); + }); +} From f9bab6a95723f28e13c2dd09278a4ad96876e403 Mon Sep 17 00:00:00 2001 From: Thierry Bela Nanga Date: Mon, 17 Aug 2026 17:26:17 -0400 Subject: [PATCH 11/22] throw an error when async parameter is passed to sync function #146 --- benchmark/package.json | 6 +- dist/index-umd-web.js | 224 +++++++-------- dist/index.cjs | 224 +++++++-------- dist/index.d.ts | 105 ++++++- dist/lib/ast/walk.js | 29 +- dist/lib/parser/parse.js | 176 ++++-------- dist/lib/renderer/sourcemap/sourcemap.js | 1 + dist/node.js | 7 +- dist/{utils.d.ts => utils/sync.d.ts} | 3 +- dist/{utils.js => utils/sync.js} | 19 +- dist/web.js | 7 +- files/assets/typedoc-custom.css | 15 +- files/plugins.md | 7 +- files/usage.md | 35 ++- src/@types/index.d.ts | 15 +- src/@types/walker.d.ts | 20 ++ src/lib/ast/walk.ts | 186 ++++++++++++- src/lib/parser/parse.ts | 338 +++++++---------------- src/lib/renderer/sourcemap/sourcemap.ts | 1 + src/lib/validation/match.ts | 82 +++--- src/node.ts | 9 +- src/{utils.ts => utils/sync.ts} | 24 +- src/web.ts | 9 +- test/specs/code/visitors.js | 48 ++++ 24 files changed, 873 insertions(+), 717 deletions(-) rename dist/{utils.d.ts => utils/sync.d.ts} (52%) rename dist/{utils.js => utils/sync.js} (67%) rename src/{utils.ts => utils/sync.ts} (65%) diff --git a/benchmark/package.json b/benchmark/package.json index a7e23016..dafde208 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -10,11 +10,11 @@ "all": "npm run sizes && npm run bench && npm run report" }, "dependencies": { - "@tbela99/css-parser": "^1.5.0", - "@tbela99/css-parser2": "github:tbela99/css-parser#2279484", + "@tbela99/css-parser": "^1.4.11", + "@tbela99/css-parser2": "github:tbela99/css-parser#2628ebce", "clean-css": "^5.3.3", "css-tree": "^3.2.1", - "cssnano": "^8.0.5", + "cssnano": "^8.0.6", "csso": "^5.0.5", "esbuild": "^0.28.2", "lightningcss": "^1.33.0", diff --git a/dist/index-umd-web.js b/dist/index-umd-web.js index ffe67ee0..0fa56d1e 100644 --- a/dist/index-umd-web.js +++ b/dist/index-umd-web.js @@ -9435,6 +9435,8 @@ * @param filter control the walk process * @param reverse walk in reverse order * + * @private + * * ```ts * * import {walk} from '@tbela99/css-parser'; @@ -9506,11 +9508,19 @@ const parents = [node]; const root = node; const map = new Map(); + let options = filter; let isNumeric = false; + let includeValues = false; let i = 0; + if (options != null && typeof options == "object") { + filter = options.filter; + reverse = options.reverse; + includeValues = options.inludeValues; + } while ((node = parents[i++])) { let option = null; if (filter != null) { + // @ts-ignore option = filter(node); isNumeric = typeof option == "number"; if (isNumeric) { @@ -9538,8 +9548,16 @@ }, }; } - if ("chi" in node && (!isNumeric || (option & exports.WalkerOptionEnum.IgnoreChildren) === 0)) { - parents.splice(i, 0, ...node.chi[reverse ? "toReversed" : "slice"]()); + if (includeValues) { + if (node[TOKENS] != null) { + parents.splice(i, 0, ...(reverse ? node[TOKENS].toReversed() : node[TOKENS])); + } + else if (Array.isArray(node.val)) { + parents.splice(i, 0, ...(reverse ? node.val.toReversed() : node.val)); + } + } + if (node["chi"] != null && (!isNumeric || (option & exports.WalkerOptionEnum.IgnoreChildren) === 0)) { + parents.splice(i, 0, ...(reverse ? node.chi.toReversed() : node.chi)); for (const child of node.chi) { map.set(child, node); } @@ -9623,11 +9641,6 @@ continue; } used.add(value); - // parents.length = 0; - // while (node != null) { - // parents.push(node); - // node = map.get(node) ?? null; - // } if (filter.fn != null && eventType & exports.WalkerEvent.Enter) { const isValid = filter.type == null || value.typ == filter.type || @@ -21597,6 +21610,7 @@ /** * * @param sourcemaps + * @private */ constructor(sourcemaps) { if (typeof sourcemaps === "string") { @@ -28711,11 +28725,23 @@ // if leading char is digit, prefix underscore (very rare) return (/^[0-9]/.test(result) ? "_" : "") + result; }); - function parseVisitors(options, valuesHandlers, preValuesHandlers, postValuesHandlers, errors, visitorsHandlersMap, preVisitorsHandlersMap, postVisitorsHandlersMap) { - const visitors = Object.entries(options.visitor); + /** + * + * @param visitorsDef + * @param errors + * @private + */ + function parseVisitors(visitorsDef, errors) { + const visitors = Object.entries(typeof visitorsDef === "function" ? [visitorsDef] : visitorsDef); let key; let value; let i; + const valuesHandlers = new Map(); + const preValuesHandlers = new Map(); + const postValuesHandlers = new Map(); + const visitorsHandlersMap = new Map(); + const preVisitorsHandlersMap = new Map(); + const postVisitorsHandlersMap = new Map(); for (i = 0; i < visitors.length; i++) { key = visitors[i][0]; value = visitors[i][1]; @@ -28803,6 +28829,29 @@ errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); } } + const allHandlers = []; + if (preVisitorsHandlersMap.size > 0) { + allHandlers.push(preVisitorsHandlersMap); + } + if (preValuesHandlers.size > 0) { + allHandlers.push(preValuesHandlers); + } + if (visitorsHandlersMap.size > 0) { + allHandlers.push(visitorsHandlersMap); + } + if (valuesHandlers.size > 0) { + allHandlers.push(valuesHandlers); + } + if (postVisitorsHandlersMap.size > 0) { + allHandlers.push(postVisitorsHandlersMap); + } + if (postValuesHandlers.size > 0) { + allHandlers.push(postValuesHandlers); + } + return { + allHandlers, + includeTokens: preValuesHandlers.size > 0 || valuesHandlers.size > 0 || postValuesHandlers.size > 0, + }; } /** * Parse css string @@ -28869,24 +28918,18 @@ }; let tokens = []; let context = ast; - ast[ROOT] = ast; - ast[LOC] = { - sta: 0, - end: 0, - srcId: options.source.id, - }; - let valuesHandlers; - let preValuesHandlers; - let postValuesHandlers; - let preVisitorsHandlersMap; - let visitorsHandlersMap; - let postVisitorsHandlersMap; let item; let node; // @ts-ignore ignore error let parensMatch = 0; let curlyBracketMatch = 0; let currentItemIndex; + // ast[ROOT] = ast; + ast[LOC] = { + sta: 0, + end: 0, + srcId: options.source.id, + }; // if (Array.isArray(iter)) { // // @ts-expect-error // iter = iter[Symbol.iterator]() as Iterator; @@ -28991,49 +29034,23 @@ } let replacement; if (options.visitor != null) { - valuesHandlers = new Map(); - preValuesHandlers = new Map(); - postValuesHandlers = new Map(); - preVisitorsHandlersMap = new Map(); - visitorsHandlersMap = new Map(); - postVisitorsHandlersMap = new Map(); - parseVisitors(options, valuesHandlers, preValuesHandlers, postValuesHandlers, errors, visitorsHandlersMap, preVisitorsHandlersMap, postVisitorsHandlersMap); + const handlers = []; + const visitors = parseVisitors(options.visitor, errors); + const subNodes = []; let parens; let genericKey; - const handlers = []; - const allHandlers = []; - if (preVisitorsHandlersMap.size > 0) { - allHandlers.push(preVisitorsHandlersMap); - } - if (preValuesHandlers.size > 0) { - allHandlers.push(preValuesHandlers); - } - if (visitorsHandlersMap.size > 0) { - allHandlers.push(visitorsHandlersMap); - } - if (valuesHandlers.size > 0) { - allHandlers.push(valuesHandlers); - } - if (postVisitorsHandlersMap.size > 0) { - allHandlers.push(postVisitorsHandlersMap); - } - if (postValuesHandlers.size > 0) { - allHandlers.push(postValuesHandlers); - } let nodes = new Array(stats.tokensCount); - const subNodes = []; let i; let k; let j; let freeBlock = 1; - const includeTokens = preValuesHandlers.size > 0 || valuesHandlers.size > 0 || postValuesHandlers.size > 0; nodes[0] = ast; for (i = 0; i < nodes.length; i++) { if (nodes[i] == null) { break; } subNodes.length = 0; - if (includeTokens) { + if (visitors.includeTokens) { switch (nodes[i].typ) { case exports.EnumToken.RuleNodeType: case exports.EnumToken.AtRuleNodeType: @@ -29081,7 +29098,7 @@ : nodes[i].typ == exports.EnumToken.KeyframesAtRuleNodeType ? camelize(nodes[i].val) : null; - for (const map of allHandlers) { + for (const map of visitors.allHandlers) { // @ts-ignore if (genericKey != null && map.has(genericKey)) { // @ts-ignore @@ -29192,19 +29209,6 @@ } } } - while (stack.length > 0 && context != ast) { - const previousNode = stack.pop(); - context = (stack[stack.length - 1] ?? ast); - // remove empty nodes - if (options.removeEmpty && - previousNode != null && - previousNode.chi.length == 0 && - context.chi[context.chi.length - 1] == previousNode) { - context.chi.pop(); - continue; - } - break; - } if (options.minify) { if (ast.chi.length > 0) { let passes = options.pass ?? 1; @@ -29777,18 +29781,6 @@ }; let tokens = []; let context = ast; - // ast[ROOT] = ast; - ast[LOC] = { - sta: 0, - end: 0, - srcId: options.source.id, - }; - let valuesHandlers; - let preValuesHandlers; - let postValuesHandlers; - let preVisitorsHandlersMap; - let visitorsHandlersMap; - let postVisitorsHandlersMap; const imports = []; let item; let node; @@ -29796,6 +29788,12 @@ let isAsync = typeof iter[Symbol.asyncIterator] === "function"; let parensMatch = 0; let curlyBracketMatch = 0; + // ast[ROOT] = ast; + ast[LOC] = { + sta: 0, + end: 0, + srcId: options.source.id, + }; if (Array.isArray(iter)) { // @ts-expect-error iter = iter[Symbol.iterator](); @@ -29957,64 +29955,24 @@ ast = expand(ast); } let replacement; - while (stack.length > 0 && context != ast) { - const previousNode = stack.pop(); - context = (stack[stack.length - 1] ?? ast); - previousNode[PARENT] = context; - // remove empty nodes - if (options.removeEmpty && - previousNode != null && - previousNode.chi.length == 0 && - context.chi[context.chi.length - 1] == previousNode) { - context.chi.pop(); - continue; - } - break; - } if (options.visitor != null) { - valuesHandlers = new Map(); - preValuesHandlers = new Map(); - postValuesHandlers = new Map(); - preVisitorsHandlersMap = new Map(); - visitorsHandlersMap = new Map(); - postVisitorsHandlersMap = new Map(); - parseVisitors(options, valuesHandlers, preValuesHandlers, postValuesHandlers, errors, visitorsHandlersMap, preVisitorsHandlersMap, postVisitorsHandlersMap); let parens; let genericKey; const handlers = []; - const allHandlers = []; - if (preVisitorsHandlersMap.size > 0) { - allHandlers.push(preVisitorsHandlersMap); - } - if (preValuesHandlers.size > 0) { - allHandlers.push(preValuesHandlers); - } - if (visitorsHandlersMap.size > 0) { - allHandlers.push(visitorsHandlersMap); - } - if (valuesHandlers.size > 0) { - allHandlers.push(valuesHandlers); - } - if (postVisitorsHandlersMap.size > 0) { - allHandlers.push(postVisitorsHandlersMap); - } - if (postValuesHandlers.size > 0) { - allHandlers.push(postValuesHandlers); - } + const visitors = parseVisitors(options.visitor, errors); let nodes = new Array(stats.tokensCount); const subNodes = []; let i; let k; let j; let freeblock = 1; - const includeTokens = preValuesHandlers.size > 0 || valuesHandlers.size > 0 || postValuesHandlers.size > 0; nodes[0] = ast; for (i = 0; i < nodes.length; i++) { if (nodes[i] == null) { break; } subNodes.length = 0; - if (includeTokens) { + if (visitors.includeTokens) { switch (nodes[i].typ) { case exports.EnumToken.RuleNodeType: case exports.EnumToken.AtRuleNodeType: @@ -30062,7 +30020,7 @@ : nodes[i].typ == exports.EnumToken.KeyframesAtRuleNodeType ? camelize(nodes[i].val) : null; - for (const map of allHandlers) { + for (const map of visitors.allHandlers) { // @ts-ignore if (genericKey != null && map.has(genericKey)) { // @ts-ignore @@ -31800,10 +31758,7 @@ } const result = parseTokens(mapped, options, errors); // remove EOF token - result.pop(); - if (result.at(-1)?.typ === exports.EnumToken.WhitespaceTokenType) { - result.pop(); - } + result.splice(result.length - (result[result.length - 2]?.typ === exports.EnumToken.WhitespaceTokenType ? 2 : 1), 2); return result; } /** @@ -31894,7 +31849,6 @@ node, location: options.source.getSourceLocation(node[LOC].sta), }); - // return []; continue; } index = tokens.indexOf(stack.at(-1)); @@ -32086,6 +32040,21 @@ } return result; } + function validateSyncArguments(options, prefix = "options.") { + const args = Object.entries(options); + let i; + for (i = 0; i < args.length; i++) { + const [key, value] = args[i]; + if (typeof value == 'function') { + if (value instanceof Promise || Object.getPrototypeOf(value).constructor.name == "AsyncFunction") { + throw new Error(`[${prefix + key}]: Async functions are not supported in sync mode. Use parse() or transform() instead.`); + } + } + else if (value != null && typeof value == 'object') { + validateSyncArguments(value, prefix + key + "."); + } + } + } /** * Load file or url @@ -32222,6 +32191,9 @@ options = opt; stream = input; } + if (options != null) { + validateSyncArguments(options); + } options ??= {}; options.src ??= ""; options.sourcesMap ??= new Map(); @@ -32249,7 +32221,7 @@ currentPosition: -1, }; const result = doParseSync(tokenize(options.parseInfo), options); - return !options.module && !options.inputSourceMap ? result : parseResult(result, options); + return !options.module && !options.inputSourceMap && !options.sourcemap ? result : parseResult(result, options); } /** * Transform CSS diff --git a/dist/index.cjs b/dist/index.cjs index f62fb5f0..8ba8ff59 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -9438,6 +9438,8 @@ exports.WalkerEvent = void 0; * @param filter control the walk process * @param reverse walk in reverse order * + * @private + * * ```ts * * import {walk} from '@tbela99/css-parser'; @@ -9509,11 +9511,19 @@ function* walk(node, filter, reverse) { const parents = [node]; const root = node; const map = new Map(); + let options = filter; let isNumeric = false; + let includeValues = false; let i = 0; + if (options != null && typeof options == "object") { + filter = options.filter; + reverse = options.reverse; + includeValues = options.inludeValues; + } while ((node = parents[i++])) { let option = null; if (filter != null) { + // @ts-ignore option = filter(node); isNumeric = typeof option == "number"; if (isNumeric) { @@ -9541,8 +9551,16 @@ function* walk(node, filter, reverse) { }, }; } - if ("chi" in node && (!isNumeric || (option & exports.WalkerOptionEnum.IgnoreChildren) === 0)) { - parents.splice(i, 0, ...node.chi[reverse ? "toReversed" : "slice"]()); + if (includeValues) { + if (node[TOKENS] != null) { + parents.splice(i, 0, ...(reverse ? node[TOKENS].toReversed() : node[TOKENS])); + } + else if (Array.isArray(node.val)) { + parents.splice(i, 0, ...(reverse ? node.val.toReversed() : node.val)); + } + } + if (node["chi"] != null && (!isNumeric || (option & exports.WalkerOptionEnum.IgnoreChildren) === 0)) { + parents.splice(i, 0, ...(reverse ? node.chi.toReversed() : node.chi)); for (const child of node.chi) { map.set(child, node); } @@ -9626,11 +9644,6 @@ function* walkValues(values, root = null, filter, reverse) { continue; } used.add(value); - // parents.length = 0; - // while (node != null) { - // parents.push(node); - // node = map.get(node) ?? null; - // } if (filter.fn != null && eventType & exports.WalkerEvent.Enter) { const isValid = filter.type == null || value.typ == filter.type || @@ -21600,6 +21613,7 @@ class SourceMap { /** * * @param sourcemaps + * @private */ constructor(sourcemaps) { if (typeof sourcemaps === "string") { @@ -28714,11 +28728,23 @@ const generateSyncScopedName = memoize((localName, filePath, pattern, hashLength // if leading char is digit, prefix underscore (very rare) return (/^[0-9]/.test(result) ? "_" : "") + result; }); -function parseVisitors(options, valuesHandlers, preValuesHandlers, postValuesHandlers, errors, visitorsHandlersMap, preVisitorsHandlersMap, postVisitorsHandlersMap) { - const visitors = Object.entries(options.visitor); +/** + * + * @param visitorsDef + * @param errors + * @private + */ +function parseVisitors(visitorsDef, errors) { + const visitors = Object.entries(typeof visitorsDef === "function" ? [visitorsDef] : visitorsDef); let key; let value; let i; + const valuesHandlers = new Map(); + const preValuesHandlers = new Map(); + const postValuesHandlers = new Map(); + const visitorsHandlersMap = new Map(); + const preVisitorsHandlersMap = new Map(); + const postVisitorsHandlersMap = new Map(); for (i = 0; i < visitors.length; i++) { key = visitors[i][0]; value = visitors[i][1]; @@ -28806,6 +28832,29 @@ function parseVisitors(options, valuesHandlers, preValuesHandlers, postValuesHan errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); } } + const allHandlers = []; + if (preVisitorsHandlersMap.size > 0) { + allHandlers.push(preVisitorsHandlersMap); + } + if (preValuesHandlers.size > 0) { + allHandlers.push(preValuesHandlers); + } + if (visitorsHandlersMap.size > 0) { + allHandlers.push(visitorsHandlersMap); + } + if (valuesHandlers.size > 0) { + allHandlers.push(valuesHandlers); + } + if (postVisitorsHandlersMap.size > 0) { + allHandlers.push(postVisitorsHandlersMap); + } + if (postValuesHandlers.size > 0) { + allHandlers.push(postValuesHandlers); + } + return { + allHandlers, + includeTokens: preValuesHandlers.size > 0 || valuesHandlers.size > 0 || postValuesHandlers.size > 0, + }; } /** * Parse css string @@ -28872,24 +28921,18 @@ function doParseSync(iter, options = {}) { }; let tokens = []; let context = ast; - ast[ROOT] = ast; - ast[LOC] = { - sta: 0, - end: 0, - srcId: options.source.id, - }; - let valuesHandlers; - let preValuesHandlers; - let postValuesHandlers; - let preVisitorsHandlersMap; - let visitorsHandlersMap; - let postVisitorsHandlersMap; let item; let node; // @ts-ignore ignore error let parensMatch = 0; let curlyBracketMatch = 0; let currentItemIndex; + // ast[ROOT] = ast; + ast[LOC] = { + sta: 0, + end: 0, + srcId: options.source.id, + }; // if (Array.isArray(iter)) { // // @ts-expect-error // iter = iter[Symbol.iterator]() as Iterator; @@ -28994,49 +29037,23 @@ function doParseSync(iter, options = {}) { } let replacement; if (options.visitor != null) { - valuesHandlers = new Map(); - preValuesHandlers = new Map(); - postValuesHandlers = new Map(); - preVisitorsHandlersMap = new Map(); - visitorsHandlersMap = new Map(); - postVisitorsHandlersMap = new Map(); - parseVisitors(options, valuesHandlers, preValuesHandlers, postValuesHandlers, errors, visitorsHandlersMap, preVisitorsHandlersMap, postVisitorsHandlersMap); + const handlers = []; + const visitors = parseVisitors(options.visitor, errors); + const subNodes = []; let parens; let genericKey; - const handlers = []; - const allHandlers = []; - if (preVisitorsHandlersMap.size > 0) { - allHandlers.push(preVisitorsHandlersMap); - } - if (preValuesHandlers.size > 0) { - allHandlers.push(preValuesHandlers); - } - if (visitorsHandlersMap.size > 0) { - allHandlers.push(visitorsHandlersMap); - } - if (valuesHandlers.size > 0) { - allHandlers.push(valuesHandlers); - } - if (postVisitorsHandlersMap.size > 0) { - allHandlers.push(postVisitorsHandlersMap); - } - if (postValuesHandlers.size > 0) { - allHandlers.push(postValuesHandlers); - } let nodes = new Array(stats.tokensCount); - const subNodes = []; let i; let k; let j; let freeBlock = 1; - const includeTokens = preValuesHandlers.size > 0 || valuesHandlers.size > 0 || postValuesHandlers.size > 0; nodes[0] = ast; for (i = 0; i < nodes.length; i++) { if (nodes[i] == null) { break; } subNodes.length = 0; - if (includeTokens) { + if (visitors.includeTokens) { switch (nodes[i].typ) { case exports.EnumToken.RuleNodeType: case exports.EnumToken.AtRuleNodeType: @@ -29084,7 +29101,7 @@ function doParseSync(iter, options = {}) { : nodes[i].typ == exports.EnumToken.KeyframesAtRuleNodeType ? camelize(nodes[i].val) : null; - for (const map of allHandlers) { + for (const map of visitors.allHandlers) { // @ts-ignore if (genericKey != null && map.has(genericKey)) { // @ts-ignore @@ -29195,19 +29212,6 @@ function doParseSync(iter, options = {}) { } } } - while (stack.length > 0 && context != ast) { - const previousNode = stack.pop(); - context = (stack[stack.length - 1] ?? ast); - // remove empty nodes - if (options.removeEmpty && - previousNode != null && - previousNode.chi.length == 0 && - context.chi[context.chi.length - 1] == previousNode) { - context.chi.pop(); - continue; - } - break; - } if (options.minify) { if (ast.chi.length > 0) { let passes = options.pass ?? 1; @@ -29780,18 +29784,6 @@ async function doParse(iter, options = {}) { }; let tokens = []; let context = ast; - // ast[ROOT] = ast; - ast[LOC] = { - sta: 0, - end: 0, - srcId: options.source.id, - }; - let valuesHandlers; - let preValuesHandlers; - let postValuesHandlers; - let preVisitorsHandlersMap; - let visitorsHandlersMap; - let postVisitorsHandlersMap; const imports = []; let item; let node; @@ -29799,6 +29791,12 @@ async function doParse(iter, options = {}) { let isAsync = typeof iter[Symbol.asyncIterator] === "function"; let parensMatch = 0; let curlyBracketMatch = 0; + // ast[ROOT] = ast; + ast[LOC] = { + sta: 0, + end: 0, + srcId: options.source.id, + }; if (Array.isArray(iter)) { // @ts-expect-error iter = iter[Symbol.iterator](); @@ -29960,64 +29958,24 @@ async function doParse(iter, options = {}) { ast = expand(ast); } let replacement; - while (stack.length > 0 && context != ast) { - const previousNode = stack.pop(); - context = (stack[stack.length - 1] ?? ast); - previousNode[PARENT] = context; - // remove empty nodes - if (options.removeEmpty && - previousNode != null && - previousNode.chi.length == 0 && - context.chi[context.chi.length - 1] == previousNode) { - context.chi.pop(); - continue; - } - break; - } if (options.visitor != null) { - valuesHandlers = new Map(); - preValuesHandlers = new Map(); - postValuesHandlers = new Map(); - preVisitorsHandlersMap = new Map(); - visitorsHandlersMap = new Map(); - postVisitorsHandlersMap = new Map(); - parseVisitors(options, valuesHandlers, preValuesHandlers, postValuesHandlers, errors, visitorsHandlersMap, preVisitorsHandlersMap, postVisitorsHandlersMap); let parens; let genericKey; const handlers = []; - const allHandlers = []; - if (preVisitorsHandlersMap.size > 0) { - allHandlers.push(preVisitorsHandlersMap); - } - if (preValuesHandlers.size > 0) { - allHandlers.push(preValuesHandlers); - } - if (visitorsHandlersMap.size > 0) { - allHandlers.push(visitorsHandlersMap); - } - if (valuesHandlers.size > 0) { - allHandlers.push(valuesHandlers); - } - if (postVisitorsHandlersMap.size > 0) { - allHandlers.push(postVisitorsHandlersMap); - } - if (postValuesHandlers.size > 0) { - allHandlers.push(postValuesHandlers); - } + const visitors = parseVisitors(options.visitor, errors); let nodes = new Array(stats.tokensCount); const subNodes = []; let i; let k; let j; let freeblock = 1; - const includeTokens = preValuesHandlers.size > 0 || valuesHandlers.size > 0 || postValuesHandlers.size > 0; nodes[0] = ast; for (i = 0; i < nodes.length; i++) { if (nodes[i] == null) { break; } subNodes.length = 0; - if (includeTokens) { + if (visitors.includeTokens) { switch (nodes[i].typ) { case exports.EnumToken.RuleNodeType: case exports.EnumToken.AtRuleNodeType: @@ -30065,7 +30023,7 @@ async function doParse(iter, options = {}) { : nodes[i].typ == exports.EnumToken.KeyframesAtRuleNodeType ? camelize(nodes[i].val) : null; - for (const map of allHandlers) { + for (const map of visitors.allHandlers) { // @ts-ignore if (genericKey != null && map.has(genericKey)) { // @ts-ignore @@ -31803,10 +31761,7 @@ function parseString(src, options = { parseColor: true }, errors) { } const result = parseTokens(mapped, options, errors); // remove EOF token - result.pop(); - if (result.at(-1)?.typ === exports.EnumToken.WhitespaceTokenType) { - result.pop(); - } + result.splice(result.length - (result[result.length - 2]?.typ === exports.EnumToken.WhitespaceTokenType ? 2 : 1), 2); return result; } /** @@ -31897,7 +31852,6 @@ function parseTokens(tokens, options, errors) { node, location: options.source.getSourceLocation(node[LOC].sta), }); - // return []; continue; } index = tokens.indexOf(stack.at(-1)); @@ -32089,6 +32043,21 @@ function parseResult(result, options) { } return result; } +function validateSyncArguments(options, prefix = "options.") { + const args = Object.entries(options); + let i; + for (i = 0; i < args.length; i++) { + const [key, value] = args[i]; + if (typeof value == 'function') { + if (value instanceof Promise || Object.getPrototypeOf(value).constructor.name == "AsyncFunction") { + throw new Error(`[${prefix + key}]: Async functions are not supported in sync mode. Use parse() or transform() instead.`); + } + } + else if (value != null && typeof value == 'object') { + validateSyncArguments(value, prefix + key + "."); + } + } +} /** * Load file or url @@ -32227,6 +32196,9 @@ function parseSync(...args) { options = opt; stream = input; } + if (options != null) { + validateSyncArguments(options); + } options ??= {}; options.src ??= ""; options.sourcesMap ??= new Map(); @@ -32252,7 +32224,7 @@ function parseSync(...args) { currentPosition: -1, }; const result = doParseSync(tokenize(options.parseInfo), options); - return !options.module && !options.inputSourceMap ? result : parseResult(result, options); + return !options.module && !options.inputSourceMap && !options.sourcemap ? result : parseResult(result, options); } /** * Transform css diff --git a/dist/index.d.ts b/dist/index.d.ts index 549223a7..695682dd 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -3171,7 +3171,7 @@ declare enum WalkerEvent { * } * * const result = await transform(css); - * for (const {node} of walk(result.ast, filter)) { + * for (const {node} of walk(result.ast, filter, false)) { * * console.error([EnumToken[node.typ]]); * } @@ -3186,6 +3186,79 @@ declare enum WalkerEvent { * ``` */ declare function walk(node: AstNode$1, filter?: WalkerFilter | null, reverse?: boolean): Generator; +/** + * Walk ast nodes + * @param node initial node + * @param filter control the walk process + * + * ```ts + * + * import {walk} from '@tbela99/css-parser'; + * + * const css = ` + * body { color: color(from var(--base-color) display-p3 r calc(g + 0.24) calc(b + 0.15)); } + * + * html, + * body { + * line-height: 1.474; + * } + * + * .ruler { + * + * height: 10px; + * } + * `; + * + * for (const {node, parent, root} of walk(ast)) { + * + * // do something with node + * } + * ``` + * + * Using a {@link filter} function to control the ast traversal. the filter function returns a value of type {@link WalkerOption}. + * + * ```ts + * import {EnumToken, transform, walk, WalkerOptionEnum} from '@tbela99/css-parser'; + * + * const css = ` + * body { color: color(from var(--base-color) display-p3 r calc(g + 0.24) calc(b + 0.15)); } + * + * html, + * body { + * line-height: 1.474; + * } + * + * .ruler { + * + * height: 10px; + * } + * `; + * + * function filter(node) { + * + * if (node.typ == EnumToken.AstRule && node.sel.includes('html')) { + * + * // skip the children of the current node + * return WalkerOptionEnum.IgnoreChildren; + * } + * } + * + * const result = await transform(css); + * for (const {node} of walk(result.ast, {filter, reverse: false})) { + * + * console.error([EnumToken[node.typ]]); + * } + * + * // [ "StyleSheetNodeType" ] + * // [ "RuleNodeType" ] + * // [ "DeclarationNodeType" ] + * // [ "RuleNodeType" ] + * // [ "DeclarationNodeType" ] + * // [ "RuleNodeType" ] + * // [ "DeclarationNodeType" ] + * ``` + */ +declare function walk(node: AstNode$1, filter?: WalkerOptions | null): Generator; /** * Walk ast node value tokens * @param values @@ -4825,6 +4898,26 @@ interface BorderRadius { keywords: string[]; } +/** + * node walker options + */ +export declare interface WalkerOptions { + + /** + * walk in reverse + */ + reverse?: boolean; + + /** + * Traverse node value tokens. If false, only traverse node children + */ + inludeValues?: boolean; + /** + * filter function to control the walk + */ + filter?: WalkerFilter; +} + /** * node walker option */ @@ -5437,7 +5530,7 @@ export declare interface ParserSyncOptions * Node visitor * {@link VisitorSyncNodeMap | VisitorSyncNodeMap[]} */ - visitor?: VisitorSyncNodeMap | VisitorSyncNodeMap[]; + visitor?: GenericVisitorAstNodeSyncHandlerMap | VisitorSyncNodeMap | VisitorSyncNodeMap[]; /** * Abort signal * @@ -5502,7 +5595,11 @@ export declare interface ParserOptions extends ParserSyncOptions, ModuleAsyncOpt * Node visitor * {@link VisitorNodeMap | VisitorNodeMap[]} */ - visitor?: VisitorNodeMap | VisitorNodeMap[]; + visitor?: + | GenericVisitorAstNodeSyncHandlerMap + | GenericVisitorAstNodeHandlerMap + | VisitorNodeMap + | VisitorNodeMap[]; } /** @@ -6751,4 +6848,4 @@ declare function transform(options: ParseInputStreamOptions & TransformOptions): declare function transform(options: ParseInputFileOptions & TransformOptions): Promise; export { ColorType$1 as ColorType, EnumAstNodeStatus$1 as EnumAstNodeStatus, EnumToken, FeatureWalkMode, ModuleCaseTransformEnum, ModuleScopeEnumOptions, ResponseType$1 as ResponseType, SourceMap, ValidationLevel, WalkerEvent, WalkerOptionEnum, cloneNode, convertColor, dirname, expand, find, findAll, findByValue, findLast, isOkLabClose, load, minify, okLabDistance, parse, parseDeclarations, parseFile, parseString, parseSync, render, renderValue as renderToken, replaceNodeOrValue, resolve, transform, transformFile, transformSync, walk, walkValues }; -export type { AddToken, AndToken, AngleToken, AstAtRule, AstComment, AstDeclaration, AstInvalidAtRule, AstInvalidDeclaration, AstInvalidRule, AstKeyframesAtRule, AstKeyframesRule, AstNode$1 as AstNode, AstNodeStatus, AstRule, AstRuleList, AstStyleSheet, AstValueMatcher, AtRuleToken, AtRuleVisitorHandler, AttrEndToken, AttrStartToken, AttrToken, Background, BackgroundAttachmentMapping, BackgroundPosition, BackgroundPositionClass, BackgroundPositionConstraints, BackgroundPositionMapping, BackgroundProperties, BackgroundRepeat, BackgroundRepeatMapping, BackgroundSize, BackgroundSizeMapping, BadCDOCommentToken, BadCommentToken, BadStringToken, BadUrlToken, BaseToken, BinaryExpressionNode, BinaryExpressionToken, BlockEndToken, BlockStartToken, Border, BorderColor, BorderColorClass, BorderProperties, BorderRadius, CDOCommentToken, ChildCombinatorToken, ClassSelectorToken, ColonToken, ColorToken, ColumnCombinatorToken, CommaToken, CommentToken, ComposesSelectorToken, ConstraintsMapping, ContainMatchToken, ContainerStyleRangeToken, CssVariableImportTokenType$1 as CssVariableImportTokenType, CssVariableMapTokenType, CssVariableToken$1 as CssVariableToken, DashMatchToken, DashedIdentToken, DeclarationVisitorHandler, DelimToken, DescendantCombinatorToken, DimensionToken, DivToken, DoubleColonToken, EOFToken, EndMatchToken, EqualMatchToken, ErrorDescription$1 as ErrorDescription, FlexToken, Font, FontFamily, FontProperties, FontWeight, FontWeightConstraints, FontWeightMapping, FractionToken, FrequencyToken, FunctionDefToken, FunctionImageToken, FunctionToken, FunctionURLToken, GenericVisitorAstNodeHandlerMap, GenericVisitorAstNodeSyncHandlerMap, GenericVisitorAsyncResult, GenericVisitorHandler, GenericVisitorResult, GenericVisitorSyncHandler, GenericVisitorSyncResult, GreaterThanOrEqualToken, GreaterThanToken, GridTemplateFuncToken, HashToken, IdentListToken, IdentToken, IfConditionToken, IfElseConditionToken, ImportantToken, IncludeMatchToken, InvalidAttrToken, InvalidClassSelectorToken, InvalidMediaQueryToken, LengthToken, LessThanOrEqualToken, LessThanToken, LineHeight, ListToken, LiteralToken, LoadResult, Map$1 as Map, MatchExpressionToken, MatchedSelector, MediaFeatureOnlyToken, MediaFeatureToken, MediaQueryConditionToken, MediaQueryUnaryFeatureToken, MediaRangeQueryToken, MinifyFeature, MinifyFeatureOptions, MinifyOptions, ModuleAsyncOptions, ModuleSyncOptions, MulToken, NameSpaceAttributeToken, NestingSelectorToken, NextSiblingCombinatorToken, NotToken, NumberToken, OptimizedSelector, OptimizedSelectorToken, OrToken, Outline, OutlineProperties, ParensEndToken, ParensStartToken, ParensToken, ParseInfo$1 as ParseInfo, ParseInputFileOptions, ParseInputOptions, ParseInputStreamOptions, ParseResult, ParseResultStats, ParseSourceOptions, ParseTokenOptions, ParserOptions, ParserSourceMapOptions, ParserSyncOptions, PercentageToken, Prefix, PropertiesConfig, PropertiesConfigProperties, PropertyListOptions, PropertyMapType, PropertySetType, PropertyType, PseudoClassFunctionToken, PseudoClassToken, PseudoElementToken, PseudoPageToken, PurpleBackgroundAttachment, RawNodeToken, RawSelectorTokens, RenderOptions, RenderResult, ResolutionToken, ResolvedPath, RuleVisitorHandler, SemiColonToken, Separator, ShorthandDef, ShorthandMapType, ShorthandProperties, ShorthandPropertyType, ShorthandType, SinglePropertyType, SinglePropertyTypeMapping, SourceLocation, SourceMapObject, StartMatchToken, StringToken, SubToken, SubsequentCombinatorToken, SupportsQueryConditionToken, SupportsQueryUnaryConditionToken, TimeToken, TimelineFunctionToken, TimingFunctionToken, Token$1 as Token, TokenSearchResult, TokenizeResult, TransformOptions, TransformResult, TransformSyncOptions, UnaryExpression, UnaryExpressionNode, UnclosedStringToken, UniversalSelectorToken, UrlToken, ValidationConfiguration, ValidationMediaFeature, ValidationOptions, ValidationResult, ValidationSelectorOptions, ValidationSyntaxNode, ValidationSyntaxResult, ValidationToken$1 as ValidationToken, Value, ValueVisitorHandler, ValueVisitorSyncHandler, VariableScopeInfo, VisitorNodeMap, VisitorSyncNodeMap, WalkAttributesResult, WalkResult, WalkerFilter, WalkerOption, WalkerValueFilter, WhenElseQueryConditionToken, WhenElseUnaryConditionToken, WhitespaceToken, WrappedValuesToken }; +export type { AddToken, AndToken, AngleToken, AstAtRule, AstComment, AstDeclaration, AstInvalidAtRule, AstInvalidDeclaration, AstInvalidRule, AstKeyframesAtRule, AstKeyframesRule, AstNode$1 as AstNode, AstNodeStatus, AstRule, AstRuleList, AstStyleSheet, AstValueMatcher, AtRuleToken, AtRuleVisitorHandler, AttrEndToken, AttrStartToken, AttrToken, Background, BackgroundAttachmentMapping, BackgroundPosition, BackgroundPositionClass, BackgroundPositionConstraints, BackgroundPositionMapping, BackgroundProperties, BackgroundRepeat, BackgroundRepeatMapping, BackgroundSize, BackgroundSizeMapping, BadCDOCommentToken, BadCommentToken, BadStringToken, BadUrlToken, BaseToken, BinaryExpressionNode, BinaryExpressionToken, BlockEndToken, BlockStartToken, Border, BorderColor, BorderColorClass, BorderProperties, BorderRadius, CDOCommentToken, ChildCombinatorToken, ClassSelectorToken, ColonToken, ColorToken, ColumnCombinatorToken, CommaToken, CommentToken, ComposesSelectorToken, ConstraintsMapping, ContainMatchToken, ContainerStyleRangeToken, CssVariableImportTokenType$1 as CssVariableImportTokenType, CssVariableMapTokenType, CssVariableToken$1 as CssVariableToken, DashMatchToken, DashedIdentToken, DeclarationVisitorHandler, DelimToken, DescendantCombinatorToken, DimensionToken, DivToken, DoubleColonToken, EOFToken, EndMatchToken, EqualMatchToken, ErrorDescription$1 as ErrorDescription, FlexToken, Font, FontFamily, FontProperties, FontWeight, FontWeightConstraints, FontWeightMapping, FractionToken, FrequencyToken, FunctionDefToken, FunctionImageToken, FunctionToken, FunctionURLToken, GenericVisitorAstNodeHandlerMap, GenericVisitorAstNodeSyncHandlerMap, GenericVisitorAsyncResult, GenericVisitorHandler, GenericVisitorResult, GenericVisitorSyncHandler, GenericVisitorSyncResult, GreaterThanOrEqualToken, GreaterThanToken, GridTemplateFuncToken, HashToken, IdentListToken, IdentToken, IfConditionToken, IfElseConditionToken, ImportantToken, IncludeMatchToken, InvalidAttrToken, InvalidClassSelectorToken, InvalidMediaQueryToken, LengthToken, LessThanOrEqualToken, LessThanToken, LineHeight, ListToken, LiteralToken, LoadResult, Map$1 as Map, MatchExpressionToken, MatchedSelector, MediaFeatureOnlyToken, MediaFeatureToken, MediaQueryConditionToken, MediaQueryUnaryFeatureToken, MediaRangeQueryToken, MinifyFeature, MinifyFeatureOptions, MinifyOptions, ModuleAsyncOptions, ModuleSyncOptions, MulToken, NameSpaceAttributeToken, NestingSelectorToken, NextSiblingCombinatorToken, NotToken, NumberToken, OptimizedSelector, OptimizedSelectorToken, OrToken, Outline, OutlineProperties, ParensEndToken, ParensStartToken, ParensToken, ParseInfo$1 as ParseInfo, ParseInputFileOptions, ParseInputOptions, ParseInputStreamOptions, ParseResult, ParseResultStats, ParseSourceOptions, ParseTokenOptions, ParserOptions, ParserSourceMapOptions, ParserSyncOptions, PercentageToken, Prefix, PropertiesConfig, PropertiesConfigProperties, PropertyListOptions, PropertyMapType, PropertySetType, PropertyType, PseudoClassFunctionToken, PseudoClassToken, PseudoElementToken, PseudoPageToken, PurpleBackgroundAttachment, RawNodeToken, RawSelectorTokens, RenderOptions, RenderResult, ResolutionToken, ResolvedPath, RuleVisitorHandler, SemiColonToken, Separator, ShorthandDef, ShorthandMapType, ShorthandProperties, ShorthandPropertyType, ShorthandType, SinglePropertyType, SinglePropertyTypeMapping, SourceLocation, SourceMapObject, StartMatchToken, StringToken, SubToken, SubsequentCombinatorToken, SupportsQueryConditionToken, SupportsQueryUnaryConditionToken, TimeToken, TimelineFunctionToken, TimingFunctionToken, Token$1 as Token, TokenSearchResult, TokenizeResult, TransformOptions, TransformResult, TransformSyncOptions, UnaryExpression, UnaryExpressionNode, UnclosedStringToken, UniversalSelectorToken, UrlToken, ValidationConfiguration, ValidationMediaFeature, ValidationOptions, ValidationResult, ValidationSelectorOptions, ValidationSyntaxNode, ValidationSyntaxResult, ValidationToken$1 as ValidationToken, Value, ValueVisitorHandler, ValueVisitorSyncHandler, VariableScopeInfo, VisitorNodeMap, VisitorSyncNodeMap, WalkAttributesResult, WalkResult, WalkerFilter, WalkerOption, WalkerOptions, WalkerValueFilter, WhenElseQueryConditionToken, WhenElseUnaryConditionToken, WhitespaceToken, WrappedValuesToken }; diff --git a/dist/lib/ast/walk.js b/dist/lib/ast/walk.js index d593519c..c7260b95 100644 --- a/dist/lib/ast/walk.js +++ b/dist/lib/ast/walk.js @@ -1,3 +1,5 @@ +import { TOKENS } from '../syntax/constants.js'; + /** * Options for the walk function */ @@ -40,6 +42,8 @@ var WalkerEvent; * @param filter control the walk process * @param reverse walk in reverse order * + * @private + * * ```ts * * import {walk} from '@tbela99/css-parser'; @@ -111,11 +115,19 @@ function* walk(node, filter, reverse) { const parents = [node]; const root = node; const map = new Map(); + let options = filter; let isNumeric = false; + let includeValues = false; let i = 0; + if (options != null && typeof options == "object") { + filter = options.filter; + reverse = options.reverse; + includeValues = options.inludeValues; + } while ((node = parents[i++])) { let option = null; if (filter != null) { + // @ts-ignore option = filter(node); isNumeric = typeof option == "number"; if (isNumeric) { @@ -143,8 +155,16 @@ function* walk(node, filter, reverse) { }, }; } - if ("chi" in node && (!isNumeric || (option & WalkerOptionEnum.IgnoreChildren) === 0)) { - parents.splice(i, 0, ...node.chi[reverse ? "toReversed" : "slice"]()); + if (includeValues) { + if (node[TOKENS] != null) { + parents.splice(i, 0, ...(reverse ? node[TOKENS].toReversed() : node[TOKENS])); + } + else if (Array.isArray(node.val)) { + parents.splice(i, 0, ...(reverse ? node.val.toReversed() : node.val)); + } + } + if (node["chi"] != null && (!isNumeric || (option & WalkerOptionEnum.IgnoreChildren) === 0)) { + parents.splice(i, 0, ...(reverse ? node.chi.toReversed() : node.chi)); for (const child of node.chi) { map.set(child, node); } @@ -228,11 +248,6 @@ function* walkValues(values, root = null, filter, reverse) { continue; } used.add(value); - // parents.length = 0; - // while (node != null) { - // parents.push(node); - // node = map.get(node) ?? null; - // } if (filter.fn != null && eventType & WalkerEvent.Enter) { const isValid = filter.type == null || value.typ == filter.type || diff --git a/dist/lib/parser/parse.js b/dist/lib/parser/parse.js index 3dffa43c..642a7b1d 100644 --- a/dist/lib/parser/parse.js +++ b/dist/lib/parser/parse.js @@ -291,11 +291,23 @@ const generateSyncScopedName = memoize((localName, filePath, pattern, hashLength // if leading char is digit, prefix underscore (very rare) return (/^[0-9]/.test(result) ? "_" : "") + result; }); -function parseVisitors(options, valuesHandlers, preValuesHandlers, postValuesHandlers, errors, visitorsHandlersMap, preVisitorsHandlersMap, postVisitorsHandlersMap) { - const visitors = Object.entries(options.visitor); +/** + * + * @param visitorsDef + * @param errors + * @private + */ +function parseVisitors(visitorsDef, errors) { + const visitors = Object.entries(typeof visitorsDef === "function" ? [visitorsDef] : visitorsDef); let key; let value; let i; + const valuesHandlers = new Map(); + const preValuesHandlers = new Map(); + const postValuesHandlers = new Map(); + const visitorsHandlersMap = new Map(); + const preVisitorsHandlersMap = new Map(); + const postVisitorsHandlersMap = new Map(); for (i = 0; i < visitors.length; i++) { key = visitors[i][0]; value = visitors[i][1]; @@ -383,6 +395,29 @@ function parseVisitors(options, valuesHandlers, preValuesHandlers, postValuesHan errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); } } + const allHandlers = []; + if (preVisitorsHandlersMap.size > 0) { + allHandlers.push(preVisitorsHandlersMap); + } + if (preValuesHandlers.size > 0) { + allHandlers.push(preValuesHandlers); + } + if (visitorsHandlersMap.size > 0) { + allHandlers.push(visitorsHandlersMap); + } + if (valuesHandlers.size > 0) { + allHandlers.push(valuesHandlers); + } + if (postVisitorsHandlersMap.size > 0) { + allHandlers.push(postVisitorsHandlersMap); + } + if (postValuesHandlers.size > 0) { + allHandlers.push(postValuesHandlers); + } + return { + allHandlers, + includeTokens: preValuesHandlers.size > 0 || valuesHandlers.size > 0 || postValuesHandlers.size > 0, + }; } /** * Parse css string @@ -449,24 +484,18 @@ function doParseSync(iter, options = {}) { }; let tokens = []; let context = ast; - ast[ROOT] = ast; - ast[LOC] = { - sta: 0, - end: 0, - srcId: options.source.id, - }; - let valuesHandlers; - let preValuesHandlers; - let postValuesHandlers; - let preVisitorsHandlersMap; - let visitorsHandlersMap; - let postVisitorsHandlersMap; let item; let node; // @ts-ignore ignore error let parensMatch = 0; let curlyBracketMatch = 0; let currentItemIndex; + // ast[ROOT] = ast; + ast[LOC] = { + sta: 0, + end: 0, + srcId: options.source.id, + }; // if (Array.isArray(iter)) { // // @ts-expect-error // iter = iter[Symbol.iterator]() as Iterator; @@ -571,49 +600,23 @@ function doParseSync(iter, options = {}) { } let replacement; if (options.visitor != null) { - valuesHandlers = new Map(); - preValuesHandlers = new Map(); - postValuesHandlers = new Map(); - preVisitorsHandlersMap = new Map(); - visitorsHandlersMap = new Map(); - postVisitorsHandlersMap = new Map(); - parseVisitors(options, valuesHandlers, preValuesHandlers, postValuesHandlers, errors, visitorsHandlersMap, preVisitorsHandlersMap, postVisitorsHandlersMap); + const handlers = []; + const visitors = parseVisitors(options.visitor, errors); + const subNodes = []; let parens; let genericKey; - const handlers = []; - const allHandlers = []; - if (preVisitorsHandlersMap.size > 0) { - allHandlers.push(preVisitorsHandlersMap); - } - if (preValuesHandlers.size > 0) { - allHandlers.push(preValuesHandlers); - } - if (visitorsHandlersMap.size > 0) { - allHandlers.push(visitorsHandlersMap); - } - if (valuesHandlers.size > 0) { - allHandlers.push(valuesHandlers); - } - if (postVisitorsHandlersMap.size > 0) { - allHandlers.push(postVisitorsHandlersMap); - } - if (postValuesHandlers.size > 0) { - allHandlers.push(postValuesHandlers); - } let nodes = new Array(stats.tokensCount); - const subNodes = []; let i; let k; let j; let freeBlock = 1; - const includeTokens = preValuesHandlers.size > 0 || valuesHandlers.size > 0 || postValuesHandlers.size > 0; nodes[0] = ast; for (i = 0; i < nodes.length; i++) { if (nodes[i] == null) { break; } subNodes.length = 0; - if (includeTokens) { + if (visitors.includeTokens) { switch (nodes[i].typ) { case EnumToken.RuleNodeType: case EnumToken.AtRuleNodeType: @@ -661,7 +664,7 @@ function doParseSync(iter, options = {}) { : nodes[i].typ == EnumToken.KeyframesAtRuleNodeType ? camelize(nodes[i].val) : null; - for (const map of allHandlers) { + for (const map of visitors.allHandlers) { // @ts-ignore if (genericKey != null && map.has(genericKey)) { // @ts-ignore @@ -772,19 +775,6 @@ function doParseSync(iter, options = {}) { } } } - while (stack.length > 0 && context != ast) { - const previousNode = stack.pop(); - context = (stack[stack.length - 1] ?? ast); - // remove empty nodes - if (options.removeEmpty && - previousNode != null && - previousNode.chi.length == 0 && - context.chi[context.chi.length - 1] == previousNode) { - context.chi.pop(); - continue; - } - break; - } if (options.minify) { if (ast.chi.length > 0) { let passes = options.pass ?? 1; @@ -1357,18 +1347,6 @@ async function doParse(iter, options = {}) { }; let tokens = []; let context = ast; - // ast[ROOT] = ast; - ast[LOC] = { - sta: 0, - end: 0, - srcId: options.source.id, - }; - let valuesHandlers; - let preValuesHandlers; - let postValuesHandlers; - let preVisitorsHandlersMap; - let visitorsHandlersMap; - let postVisitorsHandlersMap; const imports = []; let item; let node; @@ -1376,6 +1354,12 @@ async function doParse(iter, options = {}) { let isAsync = typeof iter[Symbol.asyncIterator] === "function"; let parensMatch = 0; let curlyBracketMatch = 0; + // ast[ROOT] = ast; + ast[LOC] = { + sta: 0, + end: 0, + srcId: options.source.id, + }; if (Array.isArray(iter)) { // @ts-expect-error iter = iter[Symbol.iterator](); @@ -1537,64 +1521,24 @@ async function doParse(iter, options = {}) { ast = expand(ast); } let replacement; - while (stack.length > 0 && context != ast) { - const previousNode = stack.pop(); - context = (stack[stack.length - 1] ?? ast); - previousNode[PARENT] = context; - // remove empty nodes - if (options.removeEmpty && - previousNode != null && - previousNode.chi.length == 0 && - context.chi[context.chi.length - 1] == previousNode) { - context.chi.pop(); - continue; - } - break; - } if (options.visitor != null) { - valuesHandlers = new Map(); - preValuesHandlers = new Map(); - postValuesHandlers = new Map(); - preVisitorsHandlersMap = new Map(); - visitorsHandlersMap = new Map(); - postVisitorsHandlersMap = new Map(); - parseVisitors(options, valuesHandlers, preValuesHandlers, postValuesHandlers, errors, visitorsHandlersMap, preVisitorsHandlersMap, postVisitorsHandlersMap); let parens; let genericKey; const handlers = []; - const allHandlers = []; - if (preVisitorsHandlersMap.size > 0) { - allHandlers.push(preVisitorsHandlersMap); - } - if (preValuesHandlers.size > 0) { - allHandlers.push(preValuesHandlers); - } - if (visitorsHandlersMap.size > 0) { - allHandlers.push(visitorsHandlersMap); - } - if (valuesHandlers.size > 0) { - allHandlers.push(valuesHandlers); - } - if (postVisitorsHandlersMap.size > 0) { - allHandlers.push(postVisitorsHandlersMap); - } - if (postValuesHandlers.size > 0) { - allHandlers.push(postValuesHandlers); - } + const visitors = parseVisitors(options.visitor, errors); let nodes = new Array(stats.tokensCount); const subNodes = []; let i; let k; let j; let freeblock = 1; - const includeTokens = preValuesHandlers.size > 0 || valuesHandlers.size > 0 || postValuesHandlers.size > 0; nodes[0] = ast; for (i = 0; i < nodes.length; i++) { if (nodes[i] == null) { break; } subNodes.length = 0; - if (includeTokens) { + if (visitors.includeTokens) { switch (nodes[i].typ) { case EnumToken.RuleNodeType: case EnumToken.AtRuleNodeType: @@ -1642,7 +1586,7 @@ async function doParse(iter, options = {}) { : nodes[i].typ == EnumToken.KeyframesAtRuleNodeType ? camelize(nodes[i].val) : null; - for (const map of allHandlers) { + for (const map of visitors.allHandlers) { // @ts-ignore if (genericKey != null && map.has(genericKey)) { // @ts-ignore @@ -3380,10 +3324,7 @@ function parseString(src, options = { parseColor: true }, errors) { } const result = parseTokens(mapped, options, errors); // remove EOF token - result.pop(); - if (result.at(-1)?.typ === EnumToken.WhitespaceTokenType) { - result.pop(); - } + result.splice(result.length - (result[result.length - 2]?.typ === EnumToken.WhitespaceTokenType ? 2 : 1), 2); return result; } /** @@ -3474,7 +3415,6 @@ function parseTokens(tokens, options, errors) { node, location: options.source.getSourceLocation(node[LOC].sta), }); - // return []; continue; } index = tokens.indexOf(stack.at(-1)); diff --git a/dist/lib/renderer/sourcemap/sourcemap.js b/dist/lib/renderer/sourcemap/sourcemap.js index 0099b322..ba38bf05 100644 --- a/dist/lib/renderer/sourcemap/sourcemap.js +++ b/dist/lib/renderer/sourcemap/sourcemap.js @@ -53,6 +53,7 @@ class SourceMap { /** * * @param sourcemaps + * @private */ constructor(sourcemaps) { if (typeof sourcemaps === "string") { diff --git a/dist/node.js b/dist/node.js index e30a1e25..269a3bd5 100644 --- a/dist/node.js +++ b/dist/node.js @@ -14,7 +14,7 @@ import { ResponseType } from './types.js'; import { resolve as resolve$1 } from 'node:path'; import { SourceFile } from './lib/parser/source.js'; import { cwd } from 'node:process'; -import { parseResult } from './utils.js'; +import { parseResult, validateSyncArguments } from './utils/sync.js'; export { minify } from './lib/ast/minify.js'; export { expand } from './lib/ast/expand.js'; export { WalkerEvent, WalkerOptionEnum, walk, walkValues } from './lib/ast/walk.js'; @@ -163,6 +163,9 @@ function parseSync(...args) { options = opt; stream = input; } + if (options != null) { + validateSyncArguments(options); + } options ??= {}; options.src ??= ""; options.sourcesMap ??= new Map(); @@ -188,7 +191,7 @@ function parseSync(...args) { currentPosition: -1, }; const result = doParseSync(tokenize(options.parseInfo), options); - return !options.module && !options.inputSourceMap ? result : parseResult(result, options); + return !options.module && !options.inputSourceMap && !options.sourcemap ? result : parseResult(result, options); } /** * Transform css diff --git a/dist/utils.d.ts b/dist/utils/sync.d.ts similarity index 52% rename from dist/utils.d.ts rename to dist/utils/sync.d.ts index 17fbc10f..0242b12d 100644 --- a/dist/utils.d.ts +++ b/dist/utils/sync.d.ts @@ -1,4 +1,4 @@ -import type { ParseResult, ParserOptions } from "./@types/index.d.ts"; +import type { ParseResult, ParserOptions, ParserSyncOptions } from "../@types/index.js"; /** * parse result. process input sourcemap * @param result @@ -7,3 +7,4 @@ import type { ParseResult, ParserOptions } from "./@types/index.d.ts"; * @private */ export declare function parseResult(result: ParseResult, options: ParserOptions): ParseResult; +export declare function validateSyncArguments(options: ParserSyncOptions, prefix?: string): void; diff --git a/dist/utils.js b/dist/utils/sync.js similarity index 67% rename from dist/utils.js rename to dist/utils/sync.js index e95f0443..d8b3dff7 100644 --- a/dist/utils.js +++ b/dist/utils/sync.js @@ -1,4 +1,4 @@ -import { EnumToken } from './lib/ast/types.js'; +import { EnumToken } from '../lib/ast/types.js'; /** * parse result. process input sourcemap @@ -45,5 +45,20 @@ function parseResult(result, options) { } return result; } +function validateSyncArguments(options, prefix = "options.") { + const args = Object.entries(options); + let i; + for (i = 0; i < args.length; i++) { + const [key, value] = args[i]; + if (typeof value == 'function') { + if (value instanceof Promise || Object.getPrototypeOf(value).constructor.name == "AsyncFunction") { + throw new Error(`[${prefix + key}]: Async functions are not supported in sync mode. Use parse() or transform() instead.`); + } + } + else if (value != null && typeof value == 'object') { + validateSyncArguments(value, prefix + key + "."); + } + } +} -export { parseResult }; +export { parseResult, validateSyncArguments }; diff --git a/dist/web.js b/dist/web.js index 68753355..eafdd719 100644 --- a/dist/web.js +++ b/dist/web.js @@ -8,7 +8,7 @@ import { tokenizeStream, tokenize } from './lib/parser/tokenize.js'; import { matchUrl, resolve, dirname } from './lib/fs/resolve.js'; import { ResponseType } from './types.js'; import { SourceFile } from './lib/parser/source.js'; -import { parseResult } from './utils.js'; +import { parseResult, validateSyncArguments } from './utils/sync.js'; export { minify } from './lib/ast/minify.js'; export { expand } from './lib/ast/expand.js'; export { WalkerEvent, WalkerOptionEnum, walk, walkValues } from './lib/ast/walk.js'; @@ -155,6 +155,9 @@ function parseSync(...args) { options = opt; stream = input; } + if (options != null) { + validateSyncArguments(options); + } options ??= {}; options.src ??= ""; options.sourcesMap ??= new Map(); @@ -182,7 +185,7 @@ function parseSync(...args) { currentPosition: -1, }; const result = doParseSync(tokenize(options.parseInfo), options); - return !options.module && !options.inputSourceMap ? result : parseResult(result, options); + return !options.module && !options.inputSourceMap && !options.sourcemap ? result : parseResult(result, options); } /** * Transform CSS diff --git a/files/assets/typedoc-custom.css b/files/assets/typedoc-custom.css index 01821819..979dc2d2 100644 --- a/files/assets/typedoc-custom.css +++ b/files/assets/typedoc-custom.css @@ -78,6 +78,17 @@ html[data-theme="dark"] { } } -#main-function-differences ~ table td { - text-align: center; +#main-function-differences ~ table tbody tr { + &:hover { + background-color: var(--transparent-blue) !important; + color: var(--blue-dark) !important; + } + + td { + &:first-child { + text-align: left; + } + + text-align: center; + } } diff --git a/files/plugins.md b/files/plugins.md index 6a003d31..ff9a9048 100644 --- a/files/plugins.md +++ b/files/plugins.md @@ -42,7 +42,7 @@ function toBase64(arraybuffer: Uint8Array) { } function inlineImagesPlugin(maxSize: number, extensions: string[]) { - return async function (node: FunctionURLToken, parent: AstNode) { + return async function UrlFunctionTokenType(node: FunctionURLToken, parent: AstNode) { if (parent.typ == EnumToken.DeclarationNodeType) { const t = node.chi.find( (t) => t.typ != EnumToken.WhitespaceTokenType && t.typ != EnumToken.CommaTokenType, @@ -70,7 +70,6 @@ function inlineImagesPlugin(maxSize: number, extensions: string[]) { return; } - // change node type to EnumToken.String Object.assign(t, { typ: EnumToken.StringTokenType, val: `"data:image/${matches[3].toLowerCase()};base64,${toBase64(new Uint8Array(buffer))}"`, @@ -90,9 +89,7 @@ const css = ` `; const result = await transform(css, { - visitor: { - UrlFunctionTokenType: inlineImagesPlugin(maxSize, extensions), - }, + visitor: inlineImagesPlugin(maxSize, extensions), }); console.error(result.code); diff --git a/files/usage.md b/files/usage.md index 9640acf6..eabdc87a 100644 --- a/files/usage.md +++ b/files/usage.md @@ -383,25 +383,22 @@ button { ## Difference Between Sync and Async APIs -The following features are **not supported by `parseSync()` and `transformSync()`**. - -### Unsupported Parsing Features - -* Flattening `@import` at-rules is not supported. -* The file loader `ParserOptions.load()` is not available. -* Parsing from a stream is not supported. -* Parsing with a file as the input parameter is not supported. - -### Unsupported CSS Module Features - -* The `pattern` parameter does not support the following algorithms: - - * `sha1` - * `sha256` - * `sha384` - * `sha512` -* CSS `composes` does not support composing from a file. -* Importing CSS variables from a file using `@value` is not supported. +### Parsing features comparison + +| Feature | parse() | transform() | transformSync() | ParseSync() | +| ----------------------- | ------- | ----------- | --------------- | ----------- | +| Parse from stream | ✅ | ✅ | ❌ | ❌ | +| Parse from file | ✅ | ✅ | ❌ | ❌ | +| Flatten @import at-rule | ✅ | ✅ | ❌ | ❌ | +| transformSync() | ✅ | ✅ | ❌ | ❌ | + +### CSS Module features comparison + +| Feature | parse() | transform() | transformSync() | ParseSync() | +| ---------------------------------------------------------------------- | ------- | ----------- | --------------- | ----------- | +| Algorithms supported by `pattern`:
sha1, sha256, sha384, sha512 | ✅ | ✅ | ❌ | ❌ | +| CSS `composes` from file | ✅ | ✅ | ❌ | ❌ | +| import CSS variables from file | ✅ | ✅ | ❌ | ❌ | ------ diff --git a/src/@types/index.d.ts b/src/@types/index.d.ts index 9e323ba4..d1e49554 100644 --- a/src/@types/index.d.ts +++ b/src/@types/index.d.ts @@ -1,4 +1,9 @@ -import type { VisitorSyncNodeMap, VisitorNodeMap } from "./visitor.d.ts"; +import type { + GenericVisitorAstNodeSyncHandlerMap, + GenericVisitorAstNodeHandlerMap, + VisitorSyncNodeMap, + VisitorNodeMap, +} from "./visitor.d.ts"; import type { AstAtRule, AstDeclaration, AstNode, AstRule, AstStyleSheet, SourceLocation } from "./ast.d.ts"; import { SourceMap } from "../lib/renderer/sourcemap/sourcemap.ts"; import type { PropertyListOptions } from "./parse.d.ts"; @@ -545,7 +550,7 @@ export declare interface ParserSyncOptions * Node visitor * {@link VisitorSyncNodeMap | VisitorSyncNodeMap[]} */ - visitor?: VisitorSyncNodeMap | VisitorSyncNodeMap[]; + visitor?: GenericVisitorAstNodeSyncHandlerMap | VisitorSyncNodeMap | VisitorSyncNodeMap[]; /** * Abort signal * @@ -610,7 +615,11 @@ export declare interface ParserOptions extends ParserSyncOptions, ModuleAsyncOpt * Node visitor * {@link VisitorNodeMap | VisitorNodeMap[]} */ - visitor?: VisitorNodeMap | VisitorNodeMap[]; + visitor?: + | GenericVisitorAstNodeSyncHandlerMap + | GenericVisitorAstNodeHandlerMap + | VisitorNodeMap + | VisitorNodeMap[]; } /** diff --git a/src/@types/walker.d.ts b/src/@types/walker.d.ts index c8868e69..6263d42a 100644 --- a/src/@types/walker.d.ts +++ b/src/@types/walker.d.ts @@ -2,6 +2,26 @@ import type { AstNode, AstRuleList } from "./ast.d.ts"; import type { Token } from "./token.d.ts"; import { WalkerEvent, WalkerOptionEnum } from "../lib/ast/walk.ts"; +/** + * node walker options + */ +export declare interface WalkerOptions { + + /** + * walk in reverse + */ + reverse?: boolean; + + /** + * Traverse node value tokens. If false, only traverse node children + */ + inludeValues?: boolean; + /** + * filter function to control the walk + */ + filter?: WalkerFilter; +} + /** * node walker option */ diff --git a/src/lib/ast/walk.ts b/src/lib/ast/walk.ts index e656fdba..b8e3bc52 100644 --- a/src/lib/ast/walk.ts +++ b/src/lib/ast/walk.ts @@ -8,9 +8,11 @@ import type { WalkAttributesResult, WalkerFilter, WalkerOption, + WalkerOptions, WalkerValueFilter, WalkResult, } from "../../@types/index.d.ts"; +import { TOKENS } from "../syntax/constants.ts"; import { EnumToken } from "./types.ts"; /** @@ -108,6 +110,157 @@ export enum WalkerEvent { * } * * const result = await transform(css); + * for (const {node} of walk(result.ast, filter, false)) { + * + * console.error([EnumToken[node.typ]]); + * } + * + * // [ "StyleSheetNodeType" ] + * // [ "RuleNodeType" ] + * // [ "DeclarationNodeType" ] + * // [ "RuleNodeType" ] + * // [ "DeclarationNodeType" ] + * // [ "RuleNodeType" ] + * // [ "DeclarationNodeType" ] + * ``` + */ +export function walk(node: AstNode, filter?: WalkerFilter | null, reverse?: boolean): Generator; + +/** + * Walk ast nodes + * @param node initial node + * @param filter control the walk process + * + * ```ts + * + * import {walk} from '@tbela99/css-parser'; + * + * const css = ` + * body { color: color(from var(--base-color) display-p3 r calc(g + 0.24) calc(b + 0.15)); } + * + * html, + * body { + * line-height: 1.474; + * } + * + * .ruler { + * + * height: 10px; + * } + * `; + * + * for (const {node, parent, root} of walk(ast)) { + * + * // do something with node + * } + * ``` + * + * Using a {@link filter} function to control the ast traversal. the filter function returns a value of type {@link WalkerOption}. + * + * ```ts + * import {EnumToken, transform, walk, WalkerOptionEnum} from '@tbela99/css-parser'; + * + * const css = ` + * body { color: color(from var(--base-color) display-p3 r calc(g + 0.24) calc(b + 0.15)); } + * + * html, + * body { + * line-height: 1.474; + * } + * + * .ruler { + * + * height: 10px; + * } + * `; + * + * function filter(node) { + * + * if (node.typ == EnumToken.AstRule && node.sel.includes('html')) { + * + * // skip the children of the current node + * return WalkerOptionEnum.IgnoreChildren; + * } + * } + * + * const result = await transform(css); + * for (const {node} of walk(result.ast, {filter, reverse: false})) { + * + * console.error([EnumToken[node.typ]]); + * } + * + * // [ "StyleSheetNodeType" ] + * // [ "RuleNodeType" ] + * // [ "DeclarationNodeType" ] + * // [ "RuleNodeType" ] + * // [ "DeclarationNodeType" ] + * // [ "RuleNodeType" ] + * // [ "DeclarationNodeType" ] + * ``` + */ +export function walk(node: AstNode, filter?: WalkerOptions | null): Generator; + +/** + * Walk ast nodes + * @param node initial node + * @param filter control the walk process + * @param reverse walk in reverse order + * + * @private + * + * ```ts + * + * import {walk} from '@tbela99/css-parser'; + * + * const css = ` + * body { color: color(from var(--base-color) display-p3 r calc(g + 0.24) calc(b + 0.15)); } + * + * html, + * body { + * line-height: 1.474; + * } + * + * .ruler { + * + * height: 10px; + * } + * `; + * + * for (const {node, parent, root} of walk(ast)) { + * + * // do something with node + * } + * ``` + * + * Using a {@link filter} function to control the ast traversal. the filter function returns a value of type {@link WalkerOption}. + * + * ```ts + * import {EnumToken, transform, walk, WalkerOptionEnum} from '@tbela99/css-parser'; + * + * const css = ` + * body { color: color(from var(--base-color) display-p3 r calc(g + 0.24) calc(b + 0.15)); } + * + * html, + * body { + * line-height: 1.474; + * } + * + * .ruler { + * + * height: 10px; + * } + * `; + * + * function filter(node) { + * + * if (node.typ == EnumToken.AstRule && node.sel.includes('html')) { + * + * // skip the children of the current node + * return WalkerOptionEnum.IgnoreChildren; + * } + * } + * + * const result = await transform(css); * for (const {node} of walk(result.ast, filter)) { * * console.error([EnumToken[node.typ]]); @@ -122,18 +275,31 @@ export enum WalkerEvent { * // [ "DeclarationNodeType" ] * ``` */ -export function* walk(node: AstNode, filter?: WalkerFilter | null, reverse?: boolean): Generator { +export function* walk( + node: AstNode, + filter?: WalkerFilter | WalkerOptions | null, + reverse?: boolean, +): Generator { const parents: AstNode[] = [node]; const root: AstRuleList = node; const map: Map = new Map(); + let options: WalkerOptions | null = filter as WalkerOptions | null; let isNumeric: boolean = false; + let includeValues: boolean = false; let i: number = 0; + if (options != null && typeof options == "object") { + filter = options.filter as WalkerFilter; + reverse = options.reverse; + includeValues = options.inludeValues as boolean; + } + while ((node = parents[i++])) { let option: WalkerOption = null; if (filter != null) { + // @ts-ignore option = filter(node); isNumeric = typeof option == "number"; @@ -166,8 +332,16 @@ export function* walk(node: AstNode, filter?: WalkerFilter | null, reverse?: boo }; } - if ("chi" in node && (!isNumeric || ((option as number) & WalkerOptionEnum.IgnoreChildren) === 0)) { - parents.splice(i, 0, ...((node).chi![reverse ? "toReversed" : "slice"]())); + if (includeValues) { + if (node[TOKENS] != null) { + parents.splice(i, 0, ...(reverse ? node[TOKENS]!.toReversed() : node[TOKENS])); + } else if (Array.isArray(node.val)) { + parents.splice(i, 0, ...(reverse ? node.val.toReversed() : node.val)); + } + } + + if (node["chi"] != null && (!isNumeric || ((option as number) & WalkerOptionEnum.IgnoreChildren) === 0)) { + parents.splice(i, 0, ...(reverse ? node.chi!.toReversed() : node.chi)); for (const child of (node).chi) { map.set(child, node); @@ -270,12 +444,6 @@ export function* walkValues( } used.add(value); - // parents.length = 0; - - // while (node != null) { - // parents.push(node); - // node = map.get(node) ?? null; - // } if (filter.fn != null && eventType & WalkerEvent.Enter) { const isValid: boolean = diff --git a/src/lib/parser/parse.ts b/src/lib/parser/parse.ts index fb993136..c8f5d95f 100644 --- a/src/lib/parser/parse.ts +++ b/src/lib/parser/parse.ts @@ -10,8 +10,8 @@ import type { AstAtRule, AstComment, AstDeclaration, - AstKeyframesRule, AstKeyframesAtRule, + AstKeyframesRule, AstNode, AstRule, AstRuleList, @@ -27,6 +27,7 @@ import type { ErrorDescription, FunctionToken, GenericVisitorAstNodeHandlerMap, + GenericVisitorAstNodeSyncHandlerMap, GenericVisitorHandler, GenericVisitorResult, IdentToken, @@ -44,6 +45,7 @@ import type { Token, TokenizeResult, UrlToken, + VisitorNodeMap, WhitespaceToken, } from "../../@types/index.d.ts"; import { ERRORS, LOC, pageMarginBoxType, PARENT, ROOT, STATE, TOKENS, tokensfuncDefMap } from "../syntax/constants.ts"; @@ -412,29 +414,36 @@ export const generateSyncScopedName = memoize( }, ) as (localName: string, filePath: string, pattern: string, hashLength?: number) => string; +/** + * + * @param visitorsDef + * @param errors + * @private + */ function parseVisitors( - options: ParserSyncOptions | ParserOptions, - valuesHandlers: Map>>, - preValuesHandlers: Map>>, - postValuesHandlers: Map>>, + visitorsDef: GenericVisitorHandler | GenericVisitorAstNodeSyncHandlerMap | VisitorNodeMap | VisitorNodeMap[], errors: ErrorDescription[], - visitorsHandlersMap: Map< +) { + const visitors = Object.entries(typeof visitorsDef === "function" ? [visitorsDef] : visitorsDef); + let key: string; + let value: any; + let i: number; + + const valuesHandlers: Map>> = new Map(); + const preValuesHandlers: Map>> = new Map(); + const postValuesHandlers: Map>> = new Map(); + const visitorsHandlersMap: Map< "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", Array | Record>> - >, - preVisitorsHandlersMap: Map< + > = new Map(); + const preVisitorsHandlersMap: Map< "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", Array | Record>>> - >, - postVisitorsHandlersMap: Map< + > = new Map(); + const postVisitorsHandlersMap: Map< "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", Array | Record>>> - >, -) { - const visitors = Object.entries(options.visitor!); - let key: string; - let value: any; - let i: number; + > = new Map(); for (i = 0; i < visitors.length; i++) { key = visitors[i][0]; @@ -553,6 +562,50 @@ function parseVisitors( errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); } } + const allHandlers = [] as Array< + | Map< + "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", + Array | Record>>> + > + | Map< + "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", + Array | Record>> + > + | Map>> + | Map< + EnumToken, + Array | Record>>> + > + >; + + if (preVisitorsHandlersMap!.size > 0) { + allHandlers.push(preVisitorsHandlersMap!); + } + + if (preValuesHandlers!.size > 0) { + allHandlers.push(preValuesHandlers!); + } + + if (visitorsHandlersMap!.size > 0) { + allHandlers.push(visitorsHandlersMap!); + } + + if (valuesHandlers!.size > 0) { + allHandlers.push(valuesHandlers!); + } + + if (postVisitorsHandlersMap!.size > 0) { + allHandlers.push(postVisitorsHandlersMap!); + } + + if (postValuesHandlers!.size > 0) { + allHandlers.push(postValuesHandlers!); + } + + return { + allHandlers, + includeTokens: preValuesHandlers!.size > 0 || valuesHandlers!.size > 0 || postValuesHandlers!.size > 0, + }; } /** @@ -632,46 +685,27 @@ export function doParseSync( let tokens: Token[] = []; let context: AstRuleList = ast; - ast[ROOT] = ast; - - ast[LOC] = { - sta: 0, - end: 0, - srcId: options.source!.id, - }; - - let valuesHandlers: Map>>; - let preValuesHandlers: Map>>; - let postValuesHandlers: Map>>; - let preVisitorsHandlersMap: Map< - "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", - Array | Record>>> - >; - let visitorsHandlersMap: Map< - "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", - Array | Record>> - >; - let postVisitorsHandlersMap: Map< - "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", - Array | Record>>> - >; - let item: TokenizeResult; let node: AstAtRule | AstRule | AstKeyframesRule | AstKeyframesAtRule | AstDeclaration | AstComment | null; // @ts-ignore ignore error let parensMatch: number = 0; let curlyBracketMatch: number = 0; - let currentItemIndex: number; + // ast[ROOT] = ast; + ast[LOC] = { + sta: 0, + end: 0, + srcId: options.source!.id, + }; + // if (Array.isArray(iter)) { // // @ts-expect-error // iter = iter[Symbol.iterator]() as Iterator; // } - for (currentItemIndex = 0; currentItemIndex < (iter as Array).length; currentItemIndex++ - ) { + for (currentItemIndex = 0; currentItemIndex < (iter as Array).length; currentItemIndex++) { item = (iter as Array)[currentItemIndex]; stats.bytesIn = item.bytesIn; stats.tokensCount++; @@ -793,77 +827,18 @@ export function doParseSync( let replacement: GenericVisitorResult; if (options.visitor != null) { - valuesHandlers = new Map() as Map>>; - preValuesHandlers = new Map() as Map>>; - postValuesHandlers = new Map() as Map>>; - - preVisitorsHandlersMap = new Map(); - visitorsHandlersMap = new Map(); - postVisitorsHandlersMap = new Map(); - parseVisitors( - options, - valuesHandlers, - preValuesHandlers, - postValuesHandlers, - errors, - visitorsHandlersMap, - preVisitorsHandlersMap, - postVisitorsHandlersMap, - ); + const handlers = [] as Array>; + const visitors = parseVisitors(options.visitor, errors); + const subNodes: Array = []; let parens: Token[] | null; let genericKey: string | null; - const handlers = [] as Array>; - const allHandlers = [] as Array< - | Map< - "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", - Array | Record>>> - > - | Map< - "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", - Array | Record>> - > - | Map>> - | Map< - EnumToken, - Array | Record>>> - > - >; - - if (preVisitorsHandlersMap!.size > 0) { - allHandlers.push(preVisitorsHandlersMap!); - } - - if (preValuesHandlers!.size > 0) { - allHandlers.push(preValuesHandlers!); - } - - if (visitorsHandlersMap!.size > 0) { - allHandlers.push(visitorsHandlersMap!); - } - - if (valuesHandlers!.size > 0) { - allHandlers.push(valuesHandlers!); - } - - if (postVisitorsHandlersMap!.size > 0) { - allHandlers.push(postVisitorsHandlersMap!); - } - - if (postValuesHandlers!.size > 0) { - allHandlers.push(postValuesHandlers!); - } - let nodes: AstNode[] | null = new Array(stats.tokensCount); - const subNodes: Array = []; let i: number; let k: number; let j: number; let freeBlock: number = 1; - const includeTokens: boolean = - preValuesHandlers!.size > 0 || valuesHandlers!.size > 0 || postValuesHandlers!.size > 0; - nodes[0] = ast; for (i = 0; i < nodes.length; i++) { @@ -872,7 +847,7 @@ export function doParseSync( } subNodes.length = 0; - if (includeTokens) { + if (visitors.includeTokens) { switch (nodes[i].typ) { case EnumToken.RuleNodeType: case EnumToken.AtRuleNodeType: @@ -930,7 +905,7 @@ export function doParseSync( ? camelize((nodes[i] as AstKeyframesAtRule).val) : null; - for (const map of allHandlers) { + for (const map of visitors.allHandlers) { // @ts-ignore if (genericKey != null && map!.has(genericKey)) { // @ts-ignore @@ -1062,24 +1037,6 @@ export function doParseSync( } } - while (stack.length > 0 && context != ast) { - const previousNode: AstAtRule | AstRule = stack.pop() as AstAtRule | AstRule; - context = (stack[stack.length - 1] ?? ast) as AstRuleList; - - // remove empty nodes - if ( - options.removeEmpty && - previousNode != null && - previousNode.chi!.length == 0 && - context.chi![context.chi!.length - 1] == previousNode - ) { - context.chi!.pop(); - continue; - } - - break; - } - if (options.minify) { if (ast.chi.length > 0) { let passes: number = options.pass ?? (1 as number); @@ -1816,30 +1773,6 @@ export async function doParse( let tokens: Token[] = []; let context: AstRuleList = ast; - // ast[ROOT] = ast; - - ast[LOC] = { - sta: 0, - end: 0, - srcId: options.source!.id, - }; - - let valuesHandlers: Map>>; - let preValuesHandlers: Map>>; - let postValuesHandlers: Map>>; - let preVisitorsHandlersMap: Map< - "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", - Array | Record>>> - >; - let visitorsHandlersMap: Map< - "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", - Array | Record>> - >; - let postVisitorsHandlersMap: Map< - "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", - Array | Record>>> - >; - const imports: AstAtRule[] = []; let item: TokenizeResult; @@ -1850,6 +1783,14 @@ export async function doParse( let parensMatch: number = 0; let curlyBracketMatch: number = 0; + // ast[ROOT] = ast; + + ast[LOC] = { + sta: 0, + end: 0, + srcId: options.source!.id, + }; + if (Array.isArray(iter)) { // @ts-expect-error iter = iter[Symbol.iterator]() as Iterator; @@ -2051,89 +1992,12 @@ export async function doParse( let replacement: GenericVisitorResult; - while (stack.length > 0 && context != ast) { - const previousNode: AstAtRule | AstRule = stack.pop() as AstAtRule | AstRule; - context = (stack[stack.length - 1] ?? ast) as AstRuleList; - - previousNode[PARENT] = context; - - // remove empty nodes - if ( - options.removeEmpty && - previousNode != null && - previousNode.chi!.length == 0 && - context.chi![context.chi!.length - 1] == previousNode - ) { - context.chi!.pop(); - continue; - } - - break; - } - if (options.visitor != null) { - valuesHandlers = new Map() as Map>>; - preValuesHandlers = new Map() as Map>>; - postValuesHandlers = new Map() as Map>>; - - preVisitorsHandlersMap = new Map(); - visitorsHandlersMap = new Map(); - postVisitorsHandlersMap = new Map(); - - parseVisitors( - options as ParserSyncOptions, - valuesHandlers, - preValuesHandlers, - postValuesHandlers, - errors, - visitorsHandlersMap, - preVisitorsHandlersMap, - postVisitorsHandlersMap, - ); - let parens: Token[] | null; let genericKey: string | null; const handlers = [] as Array>; - const allHandlers = [] as Array< - | Map< - "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", - Array | Record>>> - > - | Map< - "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule", - Array | Record>> - > - | Map>> - | Map< - EnumToken, - Array | Record>>> - > - >; - - if (preVisitorsHandlersMap!.size > 0) { - allHandlers.push(preVisitorsHandlersMap!); - } - - if (preValuesHandlers!.size > 0) { - allHandlers.push(preValuesHandlers!); - } - - if (visitorsHandlersMap!.size > 0) { - allHandlers.push(visitorsHandlersMap!); - } - - if (valuesHandlers!.size > 0) { - allHandlers.push(valuesHandlers!); - } - - if (postVisitorsHandlersMap!.size > 0) { - allHandlers.push(postVisitorsHandlersMap!); - } - - if (postValuesHandlers!.size > 0) { - allHandlers.push(postValuesHandlers!); - } + const visitors = parseVisitors(options.visitor, errors); let nodes: AstNode[] | null = new Array(stats.tokensCount); const subNodes: Array = []; @@ -2141,9 +2005,6 @@ export async function doParse( let k: number; let j: number; let freeblock: number = 1; - const includeTokens: boolean = - preValuesHandlers!.size > 0 || valuesHandlers!.size > 0 || postValuesHandlers!.size > 0; - nodes[0] = ast; for (i = 0; i < nodes.length; i++) { @@ -2152,7 +2013,7 @@ export async function doParse( } subNodes.length = 0; - if (includeTokens) { + if (visitors.includeTokens) { switch (nodes[i].typ) { case EnumToken.RuleNodeType: case EnumToken.AtRuleNodeType: @@ -2210,7 +2071,7 @@ export async function doParse( ? camelize((nodes[i] as AstKeyframesAtRule).val) : null; - for (const map of allHandlers) { + for (const map of visitors.allHandlers) { // @ts-ignore if (genericKey != null && map!.has(genericKey)) { // @ts-ignore @@ -4382,21 +4243,17 @@ export function parseString( currentPosition: -1, }; - const tokenResults = tokenize(parseInfo); - const mapped = []; + const tokenResults: TokenizeResult[] = tokenize(parseInfo); + const mapped: Token[] = []; for (const token of tokenResults) { mapped.push(token.token); } - const result = parseTokens(mapped, options, errors); + const result: Token[] = parseTokens(mapped, options, errors); // remove EOF token - result.pop(); - - if (result.at(-1)?.typ === EnumToken.WhitespaceTokenType) { - result.pop(); - } + result.splice(result.length - (result[result.length - 2]?.typ === EnumToken.WhitespaceTokenType ? 2 : 1), 2); return result; } @@ -4514,7 +4371,6 @@ export function parseTokens( node, location: options.source!.getSourceLocation(node[LOC]!.sta), }); - // return []; continue; } diff --git a/src/lib/renderer/sourcemap/sourcemap.ts b/src/lib/renderer/sourcemap/sourcemap.ts index a53747fc..c6dc31ca 100644 --- a/src/lib/renderer/sourcemap/sourcemap.ts +++ b/src/lib/renderer/sourcemap/sourcemap.ts @@ -71,6 +71,7 @@ export class SourceMap { /** * * @param sourcemaps + * @private */ constructor(sourcemaps?: SourceMapObject | string) { if (typeof sourcemaps === "string") { diff --git a/src/lib/validation/match.ts b/src/lib/validation/match.ts index 44164031..9341109d 100644 --- a/src/lib/validation/match.ts +++ b/src/lib/validation/match.ts @@ -60,8 +60,8 @@ export const funcTypes: EnumToken[] = [ /** * trim leading and trailing whitespace - * @param tokens - * @returns + * @param tokens + * @returns */ export function trimArray(tokens: Token[]): Token[] { while (tokens[0]?.typ === EnumToken.WhitespaceTokenType) { @@ -77,8 +77,8 @@ export function trimArray(tokens: Token[]): Token[] { /** * is a media feature - * @param featureName - * @returns + * @param featureName + * @returns */ export function isMFName(featureName: string): boolean { // @ts-expect-error @@ -206,8 +206,8 @@ export function isMFValue( /** * create validation context - * @param tokens - * @returns + * @param tokens + * @returns */ export function createValidationContext(tokens: Token[]): ValidationContext { tokens = trimArray(tokens.filter((t) => t.typ !== EnumToken.CommentTokenType)); @@ -412,11 +412,11 @@ export function createValidationContext(tokens: Token[]): ValidationContext { /** * match selector syntax - * @param stream - * @param errors - * @param options - * @param nested - * @returns + * @param stream + * @param errors + * @param options + * @param nested + * @returns */ export function matchSelectorSyntax( stream: Token[], @@ -994,10 +994,10 @@ export function matchSelectorSyntax( /** * matches all syntaxes - * @param syntaxes - * @param context - * @param options - * @returns + * @param syntaxes + * @param context + * @param options + * @returns */ export function matchAllSyntaxes( syntaxes: ValidationToken[] | null, @@ -1053,9 +1053,9 @@ export function matchAllSyntaxes( /** * matches a list of syntaxes - * @param syntax - * @param context - * @param options + * @param syntax + * @param context + * @param options * @returns */ function matchListSyntax( @@ -1121,9 +1121,9 @@ function matchListSyntax( /** * matches a list of syntaxes - * @param syntax - * @param context - * @param options + * @param syntax + * @param context + * @param options * @returns */ export function matchOccurenceSyntax( @@ -1183,10 +1183,10 @@ export function matchOccurenceSyntax( /** * matches a list of syntaxes - * @param syntaxes - * @param context - * @param options - * @returns + * @param syntaxes + * @param context + * @param options + * @returns */ function matchSyntax( syntaxes: ValidationToken[] | null, @@ -2023,10 +2023,10 @@ function matchSyntax( /** * matches a column of syntaxes - * @param syntax - * @param context - * @param options - * @returns + * @param syntax + * @param context + * @param options + * @returns */ function matchColumnSyntax( syntax: ValidationColumnToken, @@ -2080,10 +2080,10 @@ function matchColumnSyntax( /** * matches an ampersand of syntaxes - * @param syntax - * @param context - * @param options - * @returns + * @param syntax + * @param context + * @param options + * @returns */ function matchAmpersandSyntax( syntax: ValidationAmpersandToken, @@ -2116,10 +2116,10 @@ function matchAmpersandSyntax( /** * matches a property - * @param property - * @param context - * @param options - * @returns + * @param property + * @param context + * @param options + * @returns */ function matchProperty( property: ValidationPropertyToken, @@ -3075,10 +3075,10 @@ function matchProperty( /** * matches a repeatable syntax - * @param syntax - * @param context - * @param options - * @returns + * @param syntax + * @param context + * @param options + * @returns */ function matchRepeatableSyntax( syntax: ValidationToken, diff --git a/src/node.ts b/src/node.ts index 85787661..49fdddcc 100644 --- a/src/node.ts +++ b/src/node.ts @@ -27,7 +27,7 @@ import { ResponseType } from "./types.ts"; import { resolve as resolvePath } from "node:path"; import { SourceFile } from "./lib/parser/source.ts"; import { cwd } from "node:process"; -import { parseResult } from "./utils.ts"; +import { parseResult, validateSyncArguments } from "./utils/sync.ts"; export type * from "./@types/index.d.ts"; export type * from "./@types/ast.d.ts"; @@ -282,7 +282,12 @@ export function parseSync( stream = input; } + if (options != null) { + validateSyncArguments(options); + } + options ??= {}; + options.src ??= ""; options.sourcesMap ??= new Map(); @@ -312,7 +317,7 @@ export function parseSync( } as ParseInfo; const result = doParseSync(tokenize(options.parseInfo), options) as ParseResult; - return !options.module && !options.inputSourceMap ? result : parseResult(result, options); + return !options.module && !options.inputSourceMap && !options.sourcemap ? result : parseResult(result, options); } /** diff --git a/src/utils.ts b/src/utils/sync.ts similarity index 65% rename from src/utils.ts rename to src/utils/sync.ts index 11387fff..e21ea207 100644 --- a/src/utils.ts +++ b/src/utils/sync.ts @@ -1,5 +1,5 @@ -import type { AstComment, ParseResult, ParserOptions } from "./@types/index.d.ts"; -import { EnumToken } from "./lib/ast/types.ts"; +import type { AstComment, ParseResult, ParserOptions, ParserSyncOptions } from "../@types/index.js"; +import { EnumToken } from "../lib/ast/types.ts"; /** * parse result. process input sourcemap @@ -52,3 +52,23 @@ export function parseResult(result: ParseResult, options: ParserOptions): ParseR return result; } + +export function validateSyncArguments(options: ParserSyncOptions, prefix: string = "options."): void { + const args = Object.entries(options); + + let i: number; + + for (i = 0; i < args.length; i++) { + const [key, value] = args[i]; + + if (typeof value == "function") { + if (value instanceof Promise || Object.getPrototypeOf(value).constructor.name == "AsyncFunction") { + throw new Error( + `[${prefix + key}]: Async functions are not supported in sync mode. Use parse() or transform() instead.`, + ); + } + } else if (value != null && typeof value == "object") { + validateSyncArguments(value, prefix + key + "."); + } + } +} diff --git a/src/web.ts b/src/web.ts index 9d1b92e7..69abccab 100644 --- a/src/web.ts +++ b/src/web.ts @@ -22,7 +22,7 @@ import { tokenize, tokenizeStream } from "./lib/parser/tokenize.ts"; import { dirname, matchUrl, resolve } from "./lib/fs/resolve.ts"; import { ResponseType } from "./types.ts"; import { SourceFile } from "./lib/parser/source.ts"; -import { parseResult } from "./utils.ts"; +import { parseResult, validateSyncArguments } from "./utils/sync.ts"; export type * from "./@types/index.d.ts"; export type * from "./@types/ast.d.ts"; @@ -298,7 +298,12 @@ export function parseSync( stream = input; } + if (options != null) { + validateSyncArguments(options); + } + options ??= {}; + options.src ??= ""; options.sourcesMap ??= new Map(); @@ -331,7 +336,7 @@ export function parseSync( } as ParseInfo; const result = doParseSync(tokenize(options.parseInfo), options); - return !options.module && !options.inputSourceMap ? result : parseResult(result, options); + return !options.module && !options.inputSourceMap && !options.sourcemap ? result : parseResult(result, options); } /** diff --git a/test/specs/code/visitors.js b/test/specs/code/visitors.js index bda89685..66a99817 100644 --- a/test/specs/code/visitors.js +++ b/test/specs/code/visitors.js @@ -492,5 +492,53 @@ html,body { } }`); }); + + it("visitor #9", function () { + const css = ` + +body { color: color(from var(--base-color) display-p3 r calc(g + 0.24) calc(b + 0.15)); } + +html, +body { + line-height: 1.474; +} + +.ruler { + + height: 10px; + background-color: orange +} +`; + const options = { + beautify: true, + visitor: [ + function DeclarationNodeType(declaration) { + if (declaration.nam == "height") { + declaration.nam = "width"; + } + }, + function ColorTokenType(color) { + return { + typ: EnumToken.Color, + val: "red", + kin: ColorType.HEX, + }; + }, + ], + }; + + return transform(css, options).then((result) => + expect(result.code).equals(`body { + color: red +} +html,body { + line-height: 1.474 +} +.ruler { + width: 10px; + background-color: red +}`), + ); + }); }); } From b6b6f7fa37102852e7bb0f85f4753e8dcd34f889 Mon Sep 17 00:00:00 2001 From: Thierry Bela Nanga Date: Tue, 18 Aug 2026 13:51:40 -0400 Subject: [PATCH 12/22] fix sourcemap performance issue #146 --- .github/workflows/jsr.yml | 2 +- .github/workflows/node.yml | 2 +- .github/workflows/npm.yml | 4 +- .github/workflows/windows.yml | 2 +- dist/index-umd-web.js | 453 ++++++++++++++--------- dist/index.cjs | 453 ++++++++++++++--------- dist/index.d.ts | 4 +- dist/lib/ast/expand.js | 23 +- dist/lib/ast/features/prefix.js | 44 +-- dist/lib/ast/features/transform.js | 7 +- dist/lib/ast/minify.js | 13 +- dist/lib/parser/parse.js | 120 +++--- dist/lib/parser/tokenize.js | 76 ++-- dist/lib/parser/utils/selector.js | 72 +++- dist/lib/renderer/render.js | 33 +- dist/lib/renderer/sourcemap/sourcemap.js | 30 +- dist/lib/syntax/syntax.js | 42 ++- dist/utils/sync.js | 4 +- src/lib/ast/expand.ts | 31 +- src/lib/ast/features/prefix.ts | 49 +-- src/lib/ast/features/transform.ts | 8 +- src/lib/ast/minify.ts | 54 ++- src/lib/parser/parse.ts | 143 +++---- src/lib/parser/tokenize.ts | 111 +++--- src/lib/parser/utils/selector.ts | 100 ++++- src/lib/renderer/render.ts | 42 ++- src/lib/renderer/sourcemap/sourcemap.ts | 44 ++- src/lib/syntax/syntax.ts | 256 ++----------- test/allFiles.js | 6 +- test/specs/code/modules.js | 200 ++++++---- test/specs/code/prefix.js | 190 ++++++++-- test/specs/code/visitors.js | 163 +++++++- 32 files changed, 1644 insertions(+), 1137 deletions(-) diff --git a/.github/workflows/jsr.yml b/.github/workflows/jsr.yml index bbcbe825..ecec9eed 100644 --- a/.github/workflows/jsr.yml +++ b/.github/workflows/jsr.yml @@ -14,7 +14,7 @@ jobs: contents: read id-token: write # The OIDC ID token is used for authentication with JSR. steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - run: npx jsr publish diff --git a/.github/workflows/node.yml b/.github/workflows/node.yml index b583ada5..f9efd8d6 100644 --- a/.github/workflows/node.yml +++ b/.github/workflows/node.yml @@ -23,7 +23,7 @@ jobs: steps: - uses: actions/checkout@v7 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v6 + uses: actions/setup-node@v7 with: node-version: ${{ matrix.node-version }} - name: Install NPM dependencies diff --git a/.github/workflows/npm.yml b/.github/workflows/npm.yml index 2c598d04..11e7293f 100644 --- a/.github/workflows/npm.yml +++ b/.github/workflows/npm.yml @@ -10,8 +10,8 @@ jobs: contents: read id-token: write steps: - - uses: actions/checkout@v6 - - uses: actions/setup-node@v6 + - uses: actions/checkout@v7 + - uses: actions/setup-node@v7 with: node-version: '24.x' registry-url: 'https://registry.npmjs.org' diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index a829dd79..9933d689 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -23,7 +23,7 @@ jobs: steps: - uses: actions/checkout@v7 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v6 + uses: actions/setup-node@v7 with: node-version: ${{ matrix.node-version }} - name: Install NPM dependencies diff --git a/dist/index-umd-web.js b/dist/index-umd-web.js index 0fa56d1e..993990e9 100644 --- a/dist/index-umd-web.js +++ b/dist/index-umd-web.js @@ -15982,13 +15982,51 @@ } return true; }); + function isNonPrintable(codepoint) { + // null -> backspace + return ((codepoint >= 0 && codepoint <= 0x8) || + // tab + codepoint == 0xb || + // delete + codepoint == 0x7f || + (codepoint >= 0xe && codepoint <= 0x1f)); + } + function isURLToken(str) { + let i = -1; + let c; + while (++i < str.length) { + c = str.charCodeAt(i); + // single quote or double quote or start parenthesis or close parenthesis + if (isNonPrintable(c) || c == 0x27 || c == 0x22 || c == 0x28 || c == 0x29) { + return false; + } + // valid escape + if (c == REVERSE_SOLIDUS) { + i++; + if (i >= str.length) { + return false; + } + c = str.charCodeAt(i); + // c is not '\n' or '\r' or '\f' + if (c == 0x6e || c == 0x72 || c == 0x66) { + return false; + } + continue; + } + // is white space + if (c == 0x20 || c == 0x09) { + break; + } + } + return i == str.length; + } function isPseudo(name) { return (name.charAt(0) == ":" && ((name.endsWith("(") && isIdent(name.charAt(1) == ":" ? name.slice(2, -1) : name.slice(1, -1))) || isIdent(name.charAt(1) == ":" ? name.slice(2) : name.slice(1)))); } function isHash(name) { - return name.charAt(0) == "#" && isIdent(name.charAt(1)); + return name.charAt(0) == "#" && isIdentStart(name.charCodeAt(1)); } const isNumber = memoize(function (name) { // if (name.length == 0) { @@ -16339,16 +16377,19 @@ if (key in pseudoAliasMap) { const isPseudClass = pseudoAliasMap[key].startsWith("::"); value.val = pseudoAliasMap[key]; - if (value.typ == exports.EnumToken.IdenTokenType && - ["min-resolution", "max-resolution"].includes(value.val) && - parent?.typ == exports.EnumToken.MediaQueryConditionTokenType && - parent.r?.[0]?.typ == exports.EnumToken.NumberTokenType) { - Object.assign(parent.r?.[0], { - typ: exports.EnumToken.ResolutionTokenType, - unit: "x", - }); - } - else if (isPseudClass && value.typ == exports.EnumToken.PseudoElementTokenType) { + // if ( + // value.typ == EnumToken.IdenTokenType && + // ["min-resolution", "max-resolution"].includes((value as IdentToken).val) && + // parent?.typ == EnumToken.MediaQueryConditionTokenType && + // (parent as MediaQueryConditionToken).r?.[0]?.typ == EnumToken.NumberTokenType + // ) { + // Object.assign((parent as MediaQueryConditionToken).r?.[0], { + // typ: EnumToken.ResolutionTokenType, + // unit: "x", + // }); + // } + // else + if (isPseudClass && value.typ == exports.EnumToken.PseudoElementTokenType) { // @ts-ignore value.typ = exports.EnumToken.PseudoClassTokenType; } @@ -16561,32 +16602,11 @@ let commaCount; let type = ""; let tokens = token.chi.slice(); - // while ( - // i < tokens.length && - // (tokens[i].typ === EnumToken.WhitespaceTokenType || tokens[i].typ === EnumToken.CommentTokenType) - // ) { - // i++; - // } - // if (i >= tokens.length || tokens[i].typ !== EnumToken.IdenTokenType) { - // return; - // } // linear or radial if (equalsIgnoreCase(tokens[i].val, "linear")) { type = "linear-gradient"; i++; } - // else { - // return; - // } - // while ( - // i < tokens.length && - // (tokens[i].typ === EnumToken.WhitespaceTokenType || tokens[i].typ === EnumToken.CommentTokenType) - // ) { - // i++; - // } - // if (tokens[i].typ !== EnumToken.CommaTokenType) { - // return; - // } tokens.splice(0, i + 1); commaCount = 0; for (i = 0; i < tokens.length; i++) { @@ -21073,7 +21093,11 @@ } class TransformCssFeature { - accept = new Set([exports.EnumToken.RuleNodeType, exports.EnumToken.KeyframesRuleNodeType]); + accept = new Set([ + exports.EnumToken.RuleNodeType, + exports.EnumToken.AtRuleNodeType, + exports.EnumToken.KeyframesRuleNodeType, + ]); get ordering() { return 3; } @@ -21110,7 +21134,6 @@ ? minifyTransformFunctions(child) : child); } - // consumeWhitespace(children); let { matrix, cumulative, minified } = compute(children) ?? { matrix: null, cumulative: null, @@ -21633,37 +21656,47 @@ this.computePositions(); } } + hasSourceContent(id) { + return this.sourcesMap.includes(id); + } + addSourceContent(id, fileName, content) { + if (this.sourcesMap.includes(id)) { + return; + } + this.sourcesMap[this.sourcesMap.length] = id; + this.sources[this.sources.length] = fileName; + this.sourcesContent[this.sourcesContent.length] = content; + } /** * Add all location * @param maps */ addAll(maps) { - for (let [newLine, newColumn, srcId, ln, col, sourceFileName, sourceContent] of maps) { - const key = `${srcId}:${ln}:${sourceFileName}:${col}:${newLine}:${newColumn}:${sourceContent}`; - const sourcemap = `${srcId}:${sourceFileName}:${sourceContent}`; + let srcIndex; + for (let [newLine, newColumn, srcId, ln, col] of maps) { + const key = `${srcId}:${ln}:${col}:${newLine}:${newColumn}`; if (this.keys.has(key)) { continue; } this.keys.add(key); - if (!this.sourcesMap.includes(sourcemap)) { - this.sourcesMap.push(sourcemap); - this.sources.push(sourceFileName || null); - this.sourcesContent.push((sourceFileName != null ? null : sourceContent) || null); - } const line = newLine - 1; let record; if (line > this.line) { this.line = line; } + srcIndex = this.sourcesMap.indexOf(srcId); + if (srcIndex == -1) { + throw new Error(`Source file ${srcId} not added to sourcemap`); + } if (!this.map.has(line)) { - record = [Math.max(0, newColumn - 1), this.sourcesMap.indexOf(sourcemap), ln - 1, col - 1]; + record = [Math.max(0, newColumn - 1), srcIndex, ln - 1, col - 1]; this.map.set(line, [record]); } else { const arr = this.map.get(line); record = [ Math.max(0, newColumn - 1) - arr[0][0], - this.sourcesMap.indexOf(sourcemap) - arr[0][1], + srcIndex - arr[0][1], ln - 1, col - 1, ]; @@ -22439,59 +22472,43 @@ buffer = ""; value = peek(parseInfo); // consume an - while (isWhiteSpace((charCode = value.charCodeAt(0)))) { - buffer += next(parseInfo); - value = peek(parseInfo); - charCode = value.charCodeAt(0); - if (value === "/" && match(parseInfo, "/*")) { - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; - } - buffer += next(parseInfo, 2); - while ((value = next(parseInfo))) { - if (value == "*") { - buffer += value; - if (match(parseInfo, "/")) { - result.push(yieldResult(buffer + next(parseInfo), parseInfo, exports.EnumToken.CommentTokenType)); - buffer = ""; - break; - } - } - else { - buffer += value; - } - } - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo, exports.EnumToken.BadCommentTokenType)); - buffer = ""; - } + while (isWhiteSpace(peek(parseInfo).charCodeAt(0))) { + // buffer += next(parseInfo); + next(parseInfo); + // charCode = value.charCodeAt(0); + } + value = peek(parseInfo); + let values = null; + if (value == '"' || value == "'") { + values = consumeString(parseInfo); + } + else { + do { + buffer += next(parseInfo); value = peek(parseInfo); charCode = value.charCodeAt(0); - } - } - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo, exports.EnumToken.WhitespaceTokenType)); - buffer = ""; + } while ( + // !(value === "/" && match(parseInfo, "/*") && + value !== ")" && + value !== ""); } - if (value === ")" || value === '"' || value === "'") { - break; + if (values) { + if (peek(parseInfo) === "") { + for (let i = 0; i < values.length; i++) { + values[i].token.typ = exports.EnumToken.BadUrlTokenType; + } + } + result.push(...values); } - do { - buffer += next(parseInfo); - value = peek(parseInfo); - charCode = value.charCodeAt(0); - } while (value !== ")" && - !isWhiteSpace(charCode) && - !(value === "/" && match(parseInfo, "/*"))); - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo, peek(parseInfo) === "" + else if (buffer.length > 0) { + result.push(yieldResult(buffer.trimEnd(), parseInfo, + // buffer.length > 0 + peek(parseInfo) === "" || !isURLToken(buffer) ? exports.EnumToken.BadUrlTokenType : exports.EnumToken.UrlTokenTokenType)); buffer = ""; } } - // console.debug({value: peek(parseInfo)}); break; } } @@ -22736,7 +22753,7 @@ case 92 /* TokenMap.REVERSE_SOLIDUS */: next(parseInfo); // EOF - if (!(peek(parseInfo))) { + if (!peek(parseInfo)) { // end of stream ignore \\ if (buffer.length > 0) { result.push(yieldResult(buffer, parseInfo)); @@ -22839,10 +22856,8 @@ let postprocess = false; let parents; let replacement; - // @ts-ignore let { sourcemap, module, ...options2 } = options; - if (!("features" in options2)) { - // @ts-ignore + if (!(options2.features != null)) { options2 = { removeDuplicateDeclarations: true, computeShorthand: true, @@ -22892,10 +22907,9 @@ parent[PARENT] != null) { replaceNodeOrValue(parent[PARENT], parent, replacement); } - if ("chi" in replacement) { - // @ts-ignore + if (replacement.chi != null) { for (const node of replacement.chi) { - // node[PARENT] = replacement; + node[PARENT] = replacement; parents.add(node); } } @@ -22933,9 +22947,9 @@ // @ts-ignore replaceNodeOrValue(parent[PARENT], parent, replacement); } - if ("chi" in replacement) { + if (replacement.chi != null) { for (const node of replacement.chi) { - // node[PARENT] = replacement; + node[PARENT] = replacement; parents.add(node); } } @@ -24228,7 +24242,14 @@ * @private */ function expand(ast) { - const result = { ...ast, chi: [] }; + if (ast[STATE] == exports.EnumAstNodeStatus.Invalid || + ast[STATE] == exports.EnumAstNodeStatus.Disallowed || + ast[STATE] == exports.EnumAstNodeStatus.Unknown || + ast[STATE] == exports.EnumAstNodeStatus.Unparsed || + ast[STATE] == exports.EnumAstNodeStatus.Malformed) { + return ast; + } + const result = Object.assign(cloneNode(ast), { chi: [] }); let children; for (let i = 0; i < ast.chi.length; i++) { let node = ast.chi[i]; @@ -24274,7 +24295,14 @@ return result; } function expandRule(node) { - const ast = { ...node, chi: node.chi.slice() }; + if (node[STATE] == exports.EnumAstNodeStatus.Invalid || + node[STATE] == exports.EnumAstNodeStatus.Disallowed || + node[STATE] == exports.EnumAstNodeStatus.Unknown || + node[STATE] == exports.EnumAstNodeStatus.Unparsed || + node[STATE] == exports.EnumAstNodeStatus.Malformed) { + return [node]; + } + const ast = Object.assign(cloneNode(node), { chi: node.chi.slice() }); const result = []; if (ast.typ == exports.EnumToken.RuleNodeType) { let i = 0; @@ -24726,7 +24754,7 @@ const startTime = performance.now(); const errors = []; const sourcemap = options.sourcemap ? new SourceMap() : null; - const sourcemaps = options.sourcemap ? [] : null; + const sourcemaps = options.sourcemap ? { sources: [], maps: [] } : null; const cache = Object.create(null); const sourceLocation = { end: 0, @@ -24774,7 +24802,12 @@ }, }; if (sourcemap != null) { - sourcemap.addAll(sourcemaps); + let source; + for (const sourceId of sourcemaps.sources) { + source = options.sourcesMap.get(sourceId); + sourcemap.addSourceContent(source.id, source.getFileName(), source.getContent()); + } + sourcemap.addAll(sourcemaps.maps); result.map = sourcemap; if (options.sourcemap === "inline") { result.code += `\n/*# sourceMappingURL=${result.map.toUrl()} */`; @@ -24824,7 +24857,7 @@ let records = null; let srcId = node[LOC].srcId; let sourceFileName = source.getFileName() || null; - let sourceContent = source.getContent() || null; + source.getContent() || null; if (inputSourceMap != null && (records = inputSourceMap.find(offsets[0], offsets[1])) != null) { for (const record of records) { // @ts-ignore @@ -24833,7 +24866,6 @@ offsets[0] = record[1]; // @ts-ignore offsets[1] = record[2]; - sourceContent = record[3] || null; if (sourceFileName != null && options.output != null && !sourceFileName.startsWith("data:")) { if (cache[sourceFileName] == null) { const absolute = options.resolve(dirname(options.output), options.cwd) @@ -24846,7 +24878,10 @@ } sourceFileName = cache[sourceFileName]; } - sourcemaps.push([newLine, newColumn, srcId, ...offsets, sourceFileName, sourceContent]); + if (!sourcemaps.sources.includes(srcId)) { + sourcemaps.sources.push(srcId); + } + sourcemaps.maps.push([newLine, newColumn, srcId, ...offsets]); } } else { @@ -24860,7 +24895,10 @@ } sourceFileName = cache[sourceFileName]; } - sourcemaps.push([newLine, newColumn, srcId, ...offsets, sourceFileName, sourceContent]); + if (!sourcemaps.sources.includes(srcId)) { + sourcemaps.sources.push(srcId); + } + sourcemaps.maps.push([newLine, newColumn, srcId, ...offsets]); } } move(sourceLocation, linesMap, offset > 0 ? str.slice(offset) : str); @@ -25007,12 +25045,13 @@ // } // } const source = options.sourcesMap.get(node[LOC].srcId); - sourcemaps.push([ + if (!sourcemaps.sources.includes(node[LOC].srcId)) { + sourcemaps.sources.push(node[LOC].srcId); + } + sourcemaps.maps.push([ ...linesMap.getOffsets(sourceLocation.end - str.length + options.newLine.length + indentSub.length), node[LOC].srcId, ...source.getOffsets(node[LOC].sta), - source.getFileName(), - source.getContent(), ]); } } @@ -25117,8 +25156,8 @@ return " + "; case exports.EnumToken.Sub: return " - "; - case exports.EnumToken.Star: case exports.EnumToken.UniversalSelectorTokenType: + case exports.EnumToken.Star: case exports.EnumToken.Mul: return "*"; case exports.EnumToken.Div: @@ -25957,11 +25996,11 @@ case exports.EnumToken.OrTokenType: return "or"; case exports.EnumToken.InvalidMediaQueryTokenType: - // case EnumToken.InvalidDeclarationNodeType: case exports.EnumToken.InvalidCommentTokenType: case exports.EnumToken.BadCommentTokenType: case exports.EnumToken.BadCdoTokenType: case exports.EnumToken.BadStringTokenType: + case exports.EnumToken.BadUrlTokenType: case exports.EnumToken.EOFTokenType: return ""; default: @@ -26043,7 +26082,7 @@ chi: [], [LOC]: { ...tokens[0][LOC], - end: tokens[tokens.length - 1]?.[LOC]?.end ?? tokens[0]?.[LOC]?.end + end: tokens[tokens.length - 1]?.[LOC]?.end ?? tokens[0]?.[LOC]?.end, }, [TOKENS]: tokens.length === 0 ? null : tokens, [STATE]: result.success ? exports.EnumAstNodeStatus.Validated : exports.EnumAstNodeStatus.Invalid, @@ -26136,16 +26175,70 @@ } } if (tokens[i].typ == exports.EnumToken.ColorTokenType) { - // if (isIdent((tokens[i] as ColorToken).val)) { - // Object.assign(tokens[i], { - // typ: EnumToken.IdenTokenType, - // }); - // } else if (isHash(tokens[i].val)) { Object.assign(tokens[i], { typ: exports.EnumToken.HashTokenType, }); } + else { + return { + typ: exports.EnumToken.RuleNodeType, + sel: [ + ...tokens + .reduce((acc, curr, index, array) => { + // if (curr.typ == EnumToken.CommentTokenType) { + // return acc; + // } + if (curr.typ == exports.EnumToken.WhitespaceTokenType) { + if (trimWhiteSpace.includes(array[index - 1]?.typ) || + trimWhiteSpace.includes(array[index + 1]?.typ) || + combinators.includes(array[index - 1]?.val) || + combinators.includes(array[index + 1]?.val)) { + return acc; + } + } + let t = renderValue(curr, { minify: false }); + if (t == ",") { + acc.push([]); + } + else { + acc[acc.length - 1].push(t); + } + return acc; + }, [[]]) + .reduce((acc, curr) => { + let i = 0; + for (; i < curr.length; i++) { + if (i + 1 < curr.length && curr[i] == "*") { + if (curr[i] == "*") { + let index = curr[i + 1] == " " ? 2 : 1; + if (![">", "~", "+"].includes(curr[index])) { + curr.splice(i, index); + } + } + } + } + acc.set(curr.join(""), curr); + return acc; + }, uniq) + .keys(), + ].join(","), + chi: [], + [LOC]: { + ...tokens[0][LOC], + end: tokens[tokens.length - 1][LOC].end, + }, + [TOKENS]: tokens, + [STATE]: exports.EnumAstNodeStatus.Invalid, + [ERRORS]: [ + { + action: "drop", + node: tokens[i], + message: "invalid hash id", + }, + ], + }; + } } } const result = matchSelectorSyntax(tokens, errors, options, nested === true); @@ -26286,7 +26379,7 @@ // } else { // Object.assign(token, { typ: EnumToken.NumberTokenType, val: b1 }); // } - // } else + // } else if (b1 === 0) { Object.assign(token, Math.abs(a1) === 1 ? { @@ -26357,7 +26450,7 @@ // func.chi.splice(0, i); // } // break; - // } else + // } else if (num.val === 0) { func.chi.splice(index + 1, i - index); if (token.val < 0) { @@ -28482,7 +28575,6 @@ exports.EnumToken.BadStringTokenType, ]; let keyNameCounter = 0; - const forbiddenStartCharacters = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"].map((c) => c.charCodeAt(0)); /** * Short-scoped name generator. * @@ -28493,12 +28585,15 @@ * * @returns string */ - const getShortNameGenerator = memoize((localName, filePath, pattern, hashLength = 5) => { + const getShortNameGenerator = memoize(() => { let value = keyNameCounter.toString(36); + let val = value.charAt(0).charCodeAt(0); keyNameCounter++; - while (forbiddenStartCharacters.includes(value.charCodeAt(0))) { + // starts with'0' - '9' + while (48 <= val && val <= 57) { value = keyNameCounter.toString(36); keyNameCounter++; + val = value.charAt(0).charCodeAt(0); } return value; }); @@ -28746,19 +28841,19 @@ key = visitors[i][0]; value = visitors[i][1]; if (Number.isInteger(+key)) { - if (Array.isArray(value)) { - visitors.splice(i + 1, 0, ...Object.entries(value)); - continue; - } + // if (Array.isArray(value)) { + // visitors.splice(i + 1, 0, ...Object.entries(value)); + // continue; + // } if (typeof value == "function") { key = value.name; } } - if (Array.isArray(value)) { - // @ts-ignore - visitors.splice(i + 1, 0, ...value.map((item) => [key, item])); - continue; - } + // if (Array.isArray(value)) { + // // @ts-ignore + // visitors.splice(i + 1, 0, ...value.map((item) => [key, item])); + // continue; + // } if (key in exports.EnumToken) { if (typeof value == "function") { if (!valuesHandlers.has(exports.EnumToken[key])) { @@ -28766,18 +28861,27 @@ } valuesHandlers.get(exports.EnumToken[key]).push(value); } - else if (typeof value == "object" && "type" in value && "handler" in value && value.type in exports.WalkerEvent) { - if (value.type == exports.WalkerEvent.Enter) { - if (!preValuesHandlers.has(exports.EnumToken[key])) { - preValuesHandlers.set(exports.EnumToken[key], []); + else if (typeof value == "object") { + if ("type" in value && "handler" in value && value.type in exports.WalkerEvent) { + if (value.type == exports.WalkerEvent.Enter) { + if (!preValuesHandlers.has(exports.EnumToken[key])) { + preValuesHandlers.set(exports.EnumToken[key], []); + } + preValuesHandlers + .get(exports.EnumToken[key]) + .push(value.handler); } - preValuesHandlers.get(exports.EnumToken[key]).push(value.handler); - } - else if (value.type == exports.WalkerEvent.Leave) { - if (!postValuesHandlers.has(exports.EnumToken[key])) { - postValuesHandlers.set(exports.EnumToken[key], []); + else if (value.type == exports.WalkerEvent.Leave) { + if (!postValuesHandlers.has(exports.EnumToken[key])) { + postValuesHandlers.set(exports.EnumToken[key], []); + } + postValuesHandlers + .get(exports.EnumToken[key]) + .push(value.handler); } - postValuesHandlers.get(exports.EnumToken[key]).push(value.handler); + } + else { + visitors.push(...Object.entries(value)); } } else { @@ -28794,6 +28898,7 @@ .push(value); } else if (typeof value == "object") { + // visitors.push(...Object.entries(value)); if ("type" in value && "handler" in value && value.type in exports.WalkerEvent) { if (value.type == exports.WalkerEvent.Enter) { if (!preVisitorsHandlersMap.has(key)) { @@ -28930,10 +29035,6 @@ end: 0, srcId: options.source.id, }; - // if (Array.isArray(iter)) { - // // @ts-expect-error - // iter = iter[Symbol.iterator]() as Iterator; - // } for (currentItemIndex = 0; currentItemIndex < iter.length; currentItemIndex++) { item = iter[currentItemIndex]; stats.bytesIn = item.bytesIn; @@ -29106,21 +29207,20 @@ if (typeof handler == "function") { handlers.push(handler); } - else if (Array.isArray(handler)) { - for (const h of handler) { - if (typeof h == "function") { - handlers.push(h); - } - // @ts-ignore - else if (h[keyName] != null) { - // @ts-ignore - handlers.push(h[keyName]); - } - } - } - else if (typeof handler.handler == "function") { - handlers.push(handler.handler); - } + // else if (Array.isArray(handler)) { + // for (const h of handler) { + // if (typeof h == "function") { + // handlers.push(h); + // } + // // @ts-ignore + // else if (h[keyName] != null) { + // // @ts-ignore + // handlers.push(h[keyName]); + // } + // } + // } else if (typeof handler.handler! == "function") { + // handlers.push(handler.handler); + // } // @ts-ignore else if (typeof handler[keyName] == "function") { // @ts-ignore @@ -29830,8 +29930,6 @@ curlyBracketMatch--; } tokens.push(item.token); - // console.debug([item.token, {parensMatch, curlyBracketMatch}]); - // if (parensMatch === 0) { if (parensMatch === 0 && (item.token.typ === exports.EnumToken.SemiColonTokenType || item.token.typ === exports.EnumToken.BlockStartTokenType || @@ -30028,21 +30126,20 @@ if (typeof handler == "function") { handlers.push(handler); } - else if (Array.isArray(handler)) { - for (const h of handler) { - if (typeof h == "function") { - handlers.push(h); - } - // @ts-ignore - else if (h[keyName] != null) { - // @ts-ignore - handlers.push(h[keyName]); - } - } - } - else if (typeof handler.handler == "function") { - handlers.push(handler.handler); - } + // else if (Array.isArray(handler)) { + // for (const h of handler) { + // if (typeof h == "function") { + // handlers.push(h); + // } + // // @ts-ignore + // else if (h[keyName] != null) { + // // @ts-ignore + // handlers.push(h[keyName]); + // } + // } + // } else if (typeof handler.handler! == "function") { + // handlers.push(handler.handler); + // } // @ts-ignore else if (typeof handler[keyName] == "function") { // @ts-ignore @@ -32045,12 +32142,12 @@ let i; for (i = 0; i < args.length; i++) { const [key, value] = args[i]; - if (typeof value == 'function') { + if (typeof value == "function") { if (value instanceof Promise || Object.getPrototypeOf(value).constructor.name == "AsyncFunction") { throw new Error(`[${prefix + key}]: Async functions are not supported in sync mode. Use parse() or transform() instead.`); } } - else if (value != null && typeof value == 'object') { + else if (value != null && typeof value == "object") { validateSyncArguments(value, prefix + key + "."); } } diff --git a/dist/index.cjs b/dist/index.cjs index 8ba8ff59..597b006a 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -15985,13 +15985,51 @@ const isIdent = memoize(function (name) { } return true; }); +function isNonPrintable(codepoint) { + // null -> backspace + return ((codepoint >= 0 && codepoint <= 0x8) || + // tab + codepoint == 0xb || + // delete + codepoint == 0x7f || + (codepoint >= 0xe && codepoint <= 0x1f)); +} +function isURLToken(str) { + let i = -1; + let c; + while (++i < str.length) { + c = str.charCodeAt(i); + // single quote or double quote or start parenthesis or close parenthesis + if (isNonPrintable(c) || c == 0x27 || c == 0x22 || c == 0x28 || c == 0x29) { + return false; + } + // valid escape + if (c == REVERSE_SOLIDUS) { + i++; + if (i >= str.length) { + return false; + } + c = str.charCodeAt(i); + // c is not '\n' or '\r' or '\f' + if (c == 0x6e || c == 0x72 || c == 0x66) { + return false; + } + continue; + } + // is white space + if (c == 0x20 || c == 0x09) { + break; + } + } + return i == str.length; +} function isPseudo(name) { return (name.charAt(0) == ":" && ((name.endsWith("(") && isIdent(name.charAt(1) == ":" ? name.slice(2, -1) : name.slice(1, -1))) || isIdent(name.charAt(1) == ":" ? name.slice(2) : name.slice(1)))); } function isHash(name) { - return name.charAt(0) == "#" && isIdent(name.charAt(1)); + return name.charAt(0) == "#" && isIdentStart(name.charCodeAt(1)); } const isNumber = memoize(function (name) { // if (name.length == 0) { @@ -16342,16 +16380,19 @@ function replaceAstNodes(tokens, root) { if (key in pseudoAliasMap) { const isPseudClass = pseudoAliasMap[key].startsWith("::"); value.val = pseudoAliasMap[key]; - if (value.typ == exports.EnumToken.IdenTokenType && - ["min-resolution", "max-resolution"].includes(value.val) && - parent?.typ == exports.EnumToken.MediaQueryConditionTokenType && - parent.r?.[0]?.typ == exports.EnumToken.NumberTokenType) { - Object.assign(parent.r?.[0], { - typ: exports.EnumToken.ResolutionTokenType, - unit: "x", - }); - } - else if (isPseudClass && value.typ == exports.EnumToken.PseudoElementTokenType) { + // if ( + // value.typ == EnumToken.IdenTokenType && + // ["min-resolution", "max-resolution"].includes((value as IdentToken).val) && + // parent?.typ == EnumToken.MediaQueryConditionTokenType && + // (parent as MediaQueryConditionToken).r?.[0]?.typ == EnumToken.NumberTokenType + // ) { + // Object.assign((parent as MediaQueryConditionToken).r?.[0], { + // typ: EnumToken.ResolutionTokenType, + // unit: "x", + // }); + // } + // else + if (isPseudClass && value.typ == exports.EnumToken.PseudoElementTokenType) { // @ts-ignore value.typ = exports.EnumToken.PseudoClassTokenType; } @@ -16564,32 +16605,11 @@ class ComputePrefixFeature { let commaCount; let type = ""; let tokens = token.chi.slice(); - // while ( - // i < tokens.length && - // (tokens[i].typ === EnumToken.WhitespaceTokenType || tokens[i].typ === EnumToken.CommentTokenType) - // ) { - // i++; - // } - // if (i >= tokens.length || tokens[i].typ !== EnumToken.IdenTokenType) { - // return; - // } // linear or radial if (equalsIgnoreCase(tokens[i].val, "linear")) { type = "linear-gradient"; i++; } - // else { - // return; - // } - // while ( - // i < tokens.length && - // (tokens[i].typ === EnumToken.WhitespaceTokenType || tokens[i].typ === EnumToken.CommentTokenType) - // ) { - // i++; - // } - // if (tokens[i].typ !== EnumToken.CommaTokenType) { - // return; - // } tokens.splice(0, i + 1); commaCount = 0; for (i = 0; i < tokens.length; i++) { @@ -21076,7 +21096,11 @@ function splitTransformList(transformList) { } class TransformCssFeature { - accept = new Set([exports.EnumToken.RuleNodeType, exports.EnumToken.KeyframesRuleNodeType]); + accept = new Set([ + exports.EnumToken.RuleNodeType, + exports.EnumToken.AtRuleNodeType, + exports.EnumToken.KeyframesRuleNodeType, + ]); get ordering() { return 3; } @@ -21113,7 +21137,6 @@ class TransformCssFeature { ? minifyTransformFunctions(child) : child); } - // consumeWhitespace(children); let { matrix, cumulative, minified } = compute(children) ?? { matrix: null, cumulative: null, @@ -21636,37 +21659,47 @@ class SourceMap { this.computePositions(); } } + hasSourceContent(id) { + return this.sourcesMap.includes(id); + } + addSourceContent(id, fileName, content) { + if (this.sourcesMap.includes(id)) { + return; + } + this.sourcesMap[this.sourcesMap.length] = id; + this.sources[this.sources.length] = fileName; + this.sourcesContent[this.sourcesContent.length] = content; + } /** * Add all location * @param maps */ addAll(maps) { - for (let [newLine, newColumn, srcId, ln, col, sourceFileName, sourceContent] of maps) { - const key = `${srcId}:${ln}:${sourceFileName}:${col}:${newLine}:${newColumn}:${sourceContent}`; - const sourcemap = `${srcId}:${sourceFileName}:${sourceContent}`; + let srcIndex; + for (let [newLine, newColumn, srcId, ln, col] of maps) { + const key = `${srcId}:${ln}:${col}:${newLine}:${newColumn}`; if (this.keys.has(key)) { continue; } this.keys.add(key); - if (!this.sourcesMap.includes(sourcemap)) { - this.sourcesMap.push(sourcemap); - this.sources.push(sourceFileName || null); - this.sourcesContent.push((sourceFileName != null ? null : sourceContent) || null); - } const line = newLine - 1; let record; if (line > this.line) { this.line = line; } + srcIndex = this.sourcesMap.indexOf(srcId); + if (srcIndex == -1) { + throw new Error(`Source file ${srcId} not added to sourcemap`); + } if (!this.map.has(line)) { - record = [Math.max(0, newColumn - 1), this.sourcesMap.indexOf(sourcemap), ln - 1, col - 1]; + record = [Math.max(0, newColumn - 1), srcIndex, ln - 1, col - 1]; this.map.set(line, [record]); } else { const arr = this.map.get(line); record = [ Math.max(0, newColumn - 1) - arr[0][0], - this.sourcesMap.indexOf(sourcemap) - arr[0][1], + srcIndex - arr[0][1], ln - 1, col - 1, ]; @@ -22442,59 +22475,43 @@ function tokenize(parseInfo, yieldEOFToken = true) { buffer = ""; value = peek(parseInfo); // consume an - while (isWhiteSpace((charCode = value.charCodeAt(0)))) { - buffer += next(parseInfo); - value = peek(parseInfo); - charCode = value.charCodeAt(0); - if (value === "/" && match(parseInfo, "/*")) { - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; - } - buffer += next(parseInfo, 2); - while ((value = next(parseInfo))) { - if (value == "*") { - buffer += value; - if (match(parseInfo, "/")) { - result.push(yieldResult(buffer + next(parseInfo), parseInfo, exports.EnumToken.CommentTokenType)); - buffer = ""; - break; - } - } - else { - buffer += value; - } - } - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo, exports.EnumToken.BadCommentTokenType)); - buffer = ""; - } + while (isWhiteSpace(peek(parseInfo).charCodeAt(0))) { + // buffer += next(parseInfo); + next(parseInfo); + // charCode = value.charCodeAt(0); + } + value = peek(parseInfo); + let values = null; + if (value == '"' || value == "'") { + values = consumeString(parseInfo); + } + else { + do { + buffer += next(parseInfo); value = peek(parseInfo); charCode = value.charCodeAt(0); - } - } - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo, exports.EnumToken.WhitespaceTokenType)); - buffer = ""; + } while ( + // !(value === "/" && match(parseInfo, "/*") && + value !== ")" && + value !== ""); } - if (value === ")" || value === '"' || value === "'") { - break; + if (values) { + if (peek(parseInfo) === "") { + for (let i = 0; i < values.length; i++) { + values[i].token.typ = exports.EnumToken.BadUrlTokenType; + } + } + result.push(...values); } - do { - buffer += next(parseInfo); - value = peek(parseInfo); - charCode = value.charCodeAt(0); - } while (value !== ")" && - !isWhiteSpace(charCode) && - !(value === "/" && match(parseInfo, "/*"))); - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo, peek(parseInfo) === "" + else if (buffer.length > 0) { + result.push(yieldResult(buffer.trimEnd(), parseInfo, + // buffer.length > 0 + peek(parseInfo) === "" || !isURLToken(buffer) ? exports.EnumToken.BadUrlTokenType : exports.EnumToken.UrlTokenTokenType)); buffer = ""; } } - // console.debug({value: peek(parseInfo)}); break; } } @@ -22739,7 +22756,7 @@ function tokenize(parseInfo, yieldEOFToken = true) { case 92 /* TokenMap.REVERSE_SOLIDUS */: next(parseInfo); // EOF - if (!(peek(parseInfo))) { + if (!peek(parseInfo)) { // end of stream ignore \\ if (buffer.length > 0) { result.push(yieldResult(buffer, parseInfo)); @@ -22842,10 +22859,8 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent, co let postprocess = false; let parents; let replacement; - // @ts-ignore let { sourcemap, module, ...options2 } = options; - if (!("features" in options2)) { - // @ts-ignore + if (!(options2.features != null)) { options2 = { removeDuplicateDeclarations: true, computeShorthand: true, @@ -22895,10 +22910,9 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent, co parent[PARENT] != null) { replaceNodeOrValue(parent[PARENT], parent, replacement); } - if ("chi" in replacement) { - // @ts-ignore + if (replacement.chi != null) { for (const node of replacement.chi) { - // node[PARENT] = replacement; + node[PARENT] = replacement; parents.add(node); } } @@ -22936,9 +22950,9 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent, co // @ts-ignore replaceNodeOrValue(parent[PARENT], parent, replacement); } - if ("chi" in replacement) { + if (replacement.chi != null) { for (const node of replacement.chi) { - // node[PARENT] = replacement; + node[PARENT] = replacement; parents.add(node); } } @@ -24231,7 +24245,14 @@ function reduceRuleSelector(node) { * @private */ function expand(ast) { - const result = { ...ast, chi: [] }; + if (ast[STATE] == exports.EnumAstNodeStatus.Invalid || + ast[STATE] == exports.EnumAstNodeStatus.Disallowed || + ast[STATE] == exports.EnumAstNodeStatus.Unknown || + ast[STATE] == exports.EnumAstNodeStatus.Unparsed || + ast[STATE] == exports.EnumAstNodeStatus.Malformed) { + return ast; + } + const result = Object.assign(cloneNode(ast), { chi: [] }); let children; for (let i = 0; i < ast.chi.length; i++) { let node = ast.chi[i]; @@ -24277,7 +24298,14 @@ function expand(ast) { return result; } function expandRule(node) { - const ast = { ...node, chi: node.chi.slice() }; + if (node[STATE] == exports.EnumAstNodeStatus.Invalid || + node[STATE] == exports.EnumAstNodeStatus.Disallowed || + node[STATE] == exports.EnumAstNodeStatus.Unknown || + node[STATE] == exports.EnumAstNodeStatus.Unparsed || + node[STATE] == exports.EnumAstNodeStatus.Malformed) { + return [node]; + } + const ast = Object.assign(cloneNode(node), { chi: node.chi.slice() }); const result = []; if (ast.typ == exports.EnumToken.RuleNodeType) { let i = 0; @@ -24729,7 +24757,7 @@ function doRender(data, options = {}, mapping) { const startTime = performance.now(); const errors = []; const sourcemap = options.sourcemap ? new SourceMap() : null; - const sourcemaps = options.sourcemap ? [] : null; + const sourcemaps = options.sourcemap ? { sources: [], maps: [] } : null; const cache = Object.create(null); const sourceLocation = { end: 0, @@ -24777,7 +24805,12 @@ function doRender(data, options = {}, mapping) { }, }; if (sourcemap != null) { - sourcemap.addAll(sourcemaps); + let source; + for (const sourceId of sourcemaps.sources) { + source = options.sourcesMap.get(sourceId); + sourcemap.addSourceContent(source.id, source.getFileName(), source.getContent()); + } + sourcemap.addAll(sourcemaps.maps); result.map = sourcemap; if (options.sourcemap === "inline") { result.code += `\n/*# sourceMappingURL=${result.map.toUrl()} */`; @@ -24827,7 +24860,7 @@ function updateSourceMap(node, options, cache, sourcemaps, sourceLocation, lines let records = null; let srcId = node[LOC].srcId; let sourceFileName = source.getFileName() || null; - let sourceContent = source.getContent() || null; + source.getContent() || null; if (inputSourceMap != null && (records = inputSourceMap.find(offsets[0], offsets[1])) != null) { for (const record of records) { // @ts-ignore @@ -24836,7 +24869,6 @@ function updateSourceMap(node, options, cache, sourcemaps, sourceLocation, lines offsets[0] = record[1]; // @ts-ignore offsets[1] = record[2]; - sourceContent = record[3] || null; if (sourceFileName != null && options.output != null && !sourceFileName.startsWith("data:")) { if (cache[sourceFileName] == null) { const absolute = options.resolve(dirname(options.output), options.cwd) @@ -24849,7 +24881,10 @@ function updateSourceMap(node, options, cache, sourcemaps, sourceLocation, lines } sourceFileName = cache[sourceFileName]; } - sourcemaps.push([newLine, newColumn, srcId, ...offsets, sourceFileName, sourceContent]); + if (!sourcemaps.sources.includes(srcId)) { + sourcemaps.sources.push(srcId); + } + sourcemaps.maps.push([newLine, newColumn, srcId, ...offsets]); } } else { @@ -24863,7 +24898,10 @@ function updateSourceMap(node, options, cache, sourcemaps, sourceLocation, lines } sourceFileName = cache[sourceFileName]; } - sourcemaps.push([newLine, newColumn, srcId, ...offsets, sourceFileName, sourceContent]); + if (!sourcemaps.sources.includes(srcId)) { + sourcemaps.sources.push(srcId); + } + sourcemaps.maps.push([newLine, newColumn, srcId, ...offsets]); } } move(sourceLocation, linesMap, offset > 0 ? str.slice(offset) : str); @@ -25010,12 +25048,13 @@ function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, erro // } // } const source = options.sourcesMap.get(node[LOC].srcId); - sourcemaps.push([ + if (!sourcemaps.sources.includes(node[LOC].srcId)) { + sourcemaps.sources.push(node[LOC].srcId); + } + sourcemaps.maps.push([ ...linesMap.getOffsets(sourceLocation.end - str.length + options.newLine.length + indentSub.length), node[LOC].srcId, ...source.getOffsets(node[LOC].sta), - source.getFileName(), - source.getContent(), ]); } } @@ -25120,8 +25159,8 @@ function renderValue(token, options = {}, cache = Object.create(null), reducer, return " + "; case exports.EnumToken.Sub: return " - "; - case exports.EnumToken.Star: case exports.EnumToken.UniversalSelectorTokenType: + case exports.EnumToken.Star: case exports.EnumToken.Mul: return "*"; case exports.EnumToken.Div: @@ -25960,11 +25999,11 @@ function renderValue(token, options = {}, cache = Object.create(null), reducer, case exports.EnumToken.OrTokenType: return "or"; case exports.EnumToken.InvalidMediaQueryTokenType: - // case EnumToken.InvalidDeclarationNodeType: case exports.EnumToken.InvalidCommentTokenType: case exports.EnumToken.BadCommentTokenType: case exports.EnumToken.BadCdoTokenType: case exports.EnumToken.BadStringTokenType: + case exports.EnumToken.BadUrlTokenType: case exports.EnumToken.EOFTokenType: return ""; default: @@ -26046,7 +26085,7 @@ function parseSelector(tokens, context, options, errors) { chi: [], [LOC]: { ...tokens[0][LOC], - end: tokens[tokens.length - 1]?.[LOC]?.end ?? tokens[0]?.[LOC]?.end + end: tokens[tokens.length - 1]?.[LOC]?.end ?? tokens[0]?.[LOC]?.end, }, [TOKENS]: tokens.length === 0 ? null : tokens, [STATE]: result.success ? exports.EnumAstNodeStatus.Validated : exports.EnumAstNodeStatus.Invalid, @@ -26139,16 +26178,70 @@ function parseSelector(tokens, context, options, errors) { } } if (tokens[i].typ == exports.EnumToken.ColorTokenType) { - // if (isIdent((tokens[i] as ColorToken).val)) { - // Object.assign(tokens[i], { - // typ: EnumToken.IdenTokenType, - // }); - // } else if (isHash(tokens[i].val)) { Object.assign(tokens[i], { typ: exports.EnumToken.HashTokenType, }); } + else { + return { + typ: exports.EnumToken.RuleNodeType, + sel: [ + ...tokens + .reduce((acc, curr, index, array) => { + // if (curr.typ == EnumToken.CommentTokenType) { + // return acc; + // } + if (curr.typ == exports.EnumToken.WhitespaceTokenType) { + if (trimWhiteSpace.includes(array[index - 1]?.typ) || + trimWhiteSpace.includes(array[index + 1]?.typ) || + combinators.includes(array[index - 1]?.val) || + combinators.includes(array[index + 1]?.val)) { + return acc; + } + } + let t = renderValue(curr, { minify: false }); + if (t == ",") { + acc.push([]); + } + else { + acc[acc.length - 1].push(t); + } + return acc; + }, [[]]) + .reduce((acc, curr) => { + let i = 0; + for (; i < curr.length; i++) { + if (i + 1 < curr.length && curr[i] == "*") { + if (curr[i] == "*") { + let index = curr[i + 1] == " " ? 2 : 1; + if (![">", "~", "+"].includes(curr[index])) { + curr.splice(i, index); + } + } + } + } + acc.set(curr.join(""), curr); + return acc; + }, uniq) + .keys(), + ].join(","), + chi: [], + [LOC]: { + ...tokens[0][LOC], + end: tokens[tokens.length - 1][LOC].end, + }, + [TOKENS]: tokens, + [STATE]: exports.EnumAstNodeStatus.Invalid, + [ERRORS]: [ + { + action: "drop", + node: tokens[i], + message: "invalid hash id", + }, + ], + }; + } } } const result = matchSelectorSyntax(tokens, errors, options, nested === true); @@ -26289,7 +26382,7 @@ function parseSelector(tokens, context, options, errors) { // } else { // Object.assign(token, { typ: EnumToken.NumberTokenType, val: b1 }); // } - // } else + // } else if (b1 === 0) { Object.assign(token, Math.abs(a1) === 1 ? { @@ -26360,7 +26453,7 @@ function parseSelector(tokens, context, options, errors) { // func.chi.splice(0, i); // } // break; - // } else + // } else if (num.val === 0) { func.chi.splice(index + 1, i - index); if (token.val < 0) { @@ -28485,7 +28578,6 @@ const BadTokensTypes = [ exports.EnumToken.BadStringTokenType, ]; let keyNameCounter = 0; -const forbiddenStartCharacters = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"].map((c) => c.charCodeAt(0)); /** * Short-scoped name generator. * @@ -28496,12 +28588,15 @@ const forbiddenStartCharacters = ["0", "1", "2", "3", "4", "5", "6", "7", "8", " * * @returns string */ -const getShortNameGenerator = memoize((localName, filePath, pattern, hashLength = 5) => { +const getShortNameGenerator = memoize(() => { let value = keyNameCounter.toString(36); + let val = value.charAt(0).charCodeAt(0); keyNameCounter++; - while (forbiddenStartCharacters.includes(value.charCodeAt(0))) { + // starts with'0' - '9' + while (48 <= val && val <= 57) { value = keyNameCounter.toString(36); keyNameCounter++; + val = value.charAt(0).charCodeAt(0); } return value; }); @@ -28749,19 +28844,19 @@ function parseVisitors(visitorsDef, errors) { key = visitors[i][0]; value = visitors[i][1]; if (Number.isInteger(+key)) { - if (Array.isArray(value)) { - visitors.splice(i + 1, 0, ...Object.entries(value)); - continue; - } + // if (Array.isArray(value)) { + // visitors.splice(i + 1, 0, ...Object.entries(value)); + // continue; + // } if (typeof value == "function") { key = value.name; } } - if (Array.isArray(value)) { - // @ts-ignore - visitors.splice(i + 1, 0, ...value.map((item) => [key, item])); - continue; - } + // if (Array.isArray(value)) { + // // @ts-ignore + // visitors.splice(i + 1, 0, ...value.map((item) => [key, item])); + // continue; + // } if (key in exports.EnumToken) { if (typeof value == "function") { if (!valuesHandlers.has(exports.EnumToken[key])) { @@ -28769,18 +28864,27 @@ function parseVisitors(visitorsDef, errors) { } valuesHandlers.get(exports.EnumToken[key]).push(value); } - else if (typeof value == "object" && "type" in value && "handler" in value && value.type in exports.WalkerEvent) { - if (value.type == exports.WalkerEvent.Enter) { - if (!preValuesHandlers.has(exports.EnumToken[key])) { - preValuesHandlers.set(exports.EnumToken[key], []); + else if (typeof value == "object") { + if ("type" in value && "handler" in value && value.type in exports.WalkerEvent) { + if (value.type == exports.WalkerEvent.Enter) { + if (!preValuesHandlers.has(exports.EnumToken[key])) { + preValuesHandlers.set(exports.EnumToken[key], []); + } + preValuesHandlers + .get(exports.EnumToken[key]) + .push(value.handler); } - preValuesHandlers.get(exports.EnumToken[key]).push(value.handler); - } - else if (value.type == exports.WalkerEvent.Leave) { - if (!postValuesHandlers.has(exports.EnumToken[key])) { - postValuesHandlers.set(exports.EnumToken[key], []); + else if (value.type == exports.WalkerEvent.Leave) { + if (!postValuesHandlers.has(exports.EnumToken[key])) { + postValuesHandlers.set(exports.EnumToken[key], []); + } + postValuesHandlers + .get(exports.EnumToken[key]) + .push(value.handler); } - postValuesHandlers.get(exports.EnumToken[key]).push(value.handler); + } + else { + visitors.push(...Object.entries(value)); } } else { @@ -28797,6 +28901,7 @@ function parseVisitors(visitorsDef, errors) { .push(value); } else if (typeof value == "object") { + // visitors.push(...Object.entries(value)); if ("type" in value && "handler" in value && value.type in exports.WalkerEvent) { if (value.type == exports.WalkerEvent.Enter) { if (!preVisitorsHandlersMap.has(key)) { @@ -28933,10 +29038,6 @@ function doParseSync(iter, options = {}) { end: 0, srcId: options.source.id, }; - // if (Array.isArray(iter)) { - // // @ts-expect-error - // iter = iter[Symbol.iterator]() as Iterator; - // } for (currentItemIndex = 0; currentItemIndex < iter.length; currentItemIndex++) { item = iter[currentItemIndex]; stats.bytesIn = item.bytesIn; @@ -29109,21 +29210,20 @@ function doParseSync(iter, options = {}) { if (typeof handler == "function") { handlers.push(handler); } - else if (Array.isArray(handler)) { - for (const h of handler) { - if (typeof h == "function") { - handlers.push(h); - } - // @ts-ignore - else if (h[keyName] != null) { - // @ts-ignore - handlers.push(h[keyName]); - } - } - } - else if (typeof handler.handler == "function") { - handlers.push(handler.handler); - } + // else if (Array.isArray(handler)) { + // for (const h of handler) { + // if (typeof h == "function") { + // handlers.push(h); + // } + // // @ts-ignore + // else if (h[keyName] != null) { + // // @ts-ignore + // handlers.push(h[keyName]); + // } + // } + // } else if (typeof handler.handler! == "function") { + // handlers.push(handler.handler); + // } // @ts-ignore else if (typeof handler[keyName] == "function") { // @ts-ignore @@ -29833,8 +29933,6 @@ async function doParse(iter, options = {}) { curlyBracketMatch--; } tokens.push(item.token); - // console.debug([item.token, {parensMatch, curlyBracketMatch}]); - // if (parensMatch === 0) { if (parensMatch === 0 && (item.token.typ === exports.EnumToken.SemiColonTokenType || item.token.typ === exports.EnumToken.BlockStartTokenType || @@ -30031,21 +30129,20 @@ async function doParse(iter, options = {}) { if (typeof handler == "function") { handlers.push(handler); } - else if (Array.isArray(handler)) { - for (const h of handler) { - if (typeof h == "function") { - handlers.push(h); - } - // @ts-ignore - else if (h[keyName] != null) { - // @ts-ignore - handlers.push(h[keyName]); - } - } - } - else if (typeof handler.handler == "function") { - handlers.push(handler.handler); - } + // else if (Array.isArray(handler)) { + // for (const h of handler) { + // if (typeof h == "function") { + // handlers.push(h); + // } + // // @ts-ignore + // else if (h[keyName] != null) { + // // @ts-ignore + // handlers.push(h[keyName]); + // } + // } + // } else if (typeof handler.handler! == "function") { + // handlers.push(handler.handler); + // } // @ts-ignore else if (typeof handler[keyName] == "function") { // @ts-ignore @@ -32048,12 +32145,12 @@ function validateSyncArguments(options, prefix = "options.") { let i; for (i = 0; i < args.length; i++) { const [key, value] = args[i]; - if (typeof value == 'function') { + if (typeof value == "function") { if (value instanceof Promise || Object.getPrototypeOf(value).constructor.name == "AsyncFunction") { throw new Error(`[${prefix + key}]: Async functions are not supported in sync mode. Use parse() or transform() instead.`); } } - else if (value != null && typeof value == 'object') { + else if (value != null && typeof value == "object") { validateSyncArguments(value, prefix + key + "."); } } diff --git a/dist/index.d.ts b/dist/index.d.ts index 695682dd..e57f2de0 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -3970,11 +3970,13 @@ declare class SourceMap { * @param sourcemaps */ constructor(sourcemaps: string | SourceMapObject); + hasSourceContent(id: number): boolean; + addSourceContent(id: number, fileName: string | null, content: string | null): void; /** * Add all location * @param maps */ - addAll(maps: Array<[number, number, number, number, number, string | null, string | null]>): void; + addAll(maps: Array<[number, number, number, number, number]>): void; /** * compute original positions */ diff --git a/dist/lib/ast/expand.js b/dist/lib/ast/expand.js index 4da8070f..08a2c2d3 100644 --- a/dist/lib/ast/expand.js +++ b/dist/lib/ast/expand.js @@ -1,9 +1,10 @@ import { splitRule } from './minify.js'; -import { PARENT, combinators, RAW } from '../syntax/constants.js'; +import { STATE, PARENT, combinators, RAW } from '../syntax/constants.js'; import { parseString } from '../parser/parse.js'; import { walkValues } from './walk.js'; import { renderValue } from '../renderer/render.js'; -import { EnumToken } from './types.js'; +import { EnumAstNodeStatus, EnumToken } from './types.js'; +import { cloneNode } from './clone.js'; /** * expand css nesting ast nodes @@ -12,7 +13,14 @@ import { EnumToken } from './types.js'; * @private */ function expand(ast) { - const result = { ...ast, chi: [] }; + if (ast[STATE] == EnumAstNodeStatus.Invalid || + ast[STATE] == EnumAstNodeStatus.Disallowed || + ast[STATE] == EnumAstNodeStatus.Unknown || + ast[STATE] == EnumAstNodeStatus.Unparsed || + ast[STATE] == EnumAstNodeStatus.Malformed) { + return ast; + } + const result = Object.assign(cloneNode(ast), { chi: [] }); let children; for (let i = 0; i < ast.chi.length; i++) { let node = ast.chi[i]; @@ -58,7 +66,14 @@ function expand(ast) { return result; } function expandRule(node) { - const ast = { ...node, chi: node.chi.slice() }; + if (node[STATE] == EnumAstNodeStatus.Invalid || + node[STATE] == EnumAstNodeStatus.Disallowed || + node[STATE] == EnumAstNodeStatus.Unknown || + node[STATE] == EnumAstNodeStatus.Unparsed || + node[STATE] == EnumAstNodeStatus.Malformed) { + return [node]; + } + const ast = Object.assign(cloneNode(node), { chi: node.chi.slice() }); const result = []; if (ast.typ == EnumToken.RuleNodeType) { let i = 0; diff --git a/dist/lib/ast/features/prefix.js b/dist/lib/ast/features/prefix.js index d55df621..44b5031f 100644 --- a/dist/lib/ast/features/prefix.js +++ b/dist/lib/ast/features/prefix.js @@ -57,16 +57,19 @@ function replaceAstNodes(tokens, root) { if (key in pseudoAliasMap) { const isPseudClass = pseudoAliasMap[key].startsWith("::"); value.val = pseudoAliasMap[key]; - if (value.typ == EnumToken.IdenTokenType && - ["min-resolution", "max-resolution"].includes(value.val) && - parent?.typ == EnumToken.MediaQueryConditionTokenType && - parent.r?.[0]?.typ == EnumToken.NumberTokenType) { - Object.assign(parent.r?.[0], { - typ: EnumToken.ResolutionTokenType, - unit: "x", - }); - } - else if (isPseudClass && value.typ == EnumToken.PseudoElementTokenType) { + // if ( + // value.typ == EnumToken.IdenTokenType && + // ["min-resolution", "max-resolution"].includes((value as IdentToken).val) && + // parent?.typ == EnumToken.MediaQueryConditionTokenType && + // (parent as MediaQueryConditionToken).r?.[0]?.typ == EnumToken.NumberTokenType + // ) { + // Object.assign((parent as MediaQueryConditionToken).r?.[0], { + // typ: EnumToken.ResolutionTokenType, + // unit: "x", + // }); + // } + // else + if (isPseudClass && value.typ == EnumToken.PseudoElementTokenType) { // @ts-ignore value.typ = EnumToken.PseudoClassTokenType; } @@ -279,32 +282,11 @@ class ComputePrefixFeature { let commaCount; let type = ""; let tokens = token.chi.slice(); - // while ( - // i < tokens.length && - // (tokens[i].typ === EnumToken.WhitespaceTokenType || tokens[i].typ === EnumToken.CommentTokenType) - // ) { - // i++; - // } - // if (i >= tokens.length || tokens[i].typ !== EnumToken.IdenTokenType) { - // return; - // } // linear or radial if (equalsIgnoreCase(tokens[i].val, "linear")) { type = "linear-gradient"; i++; } - // else { - // return; - // } - // while ( - // i < tokens.length && - // (tokens[i].typ === EnumToken.WhitespaceTokenType || tokens[i].typ === EnumToken.CommentTokenType) - // ) { - // i++; - // } - // if (tokens[i].typ !== EnumToken.CommaTokenType) { - // return; - // } tokens.splice(0, i + 1); commaCount = 0; for (i = 0; i < tokens.length; i++) { diff --git a/dist/lib/ast/features/transform.js b/dist/lib/ast/features/transform.js index 0009cfe3..a0306f8a 100644 --- a/dist/lib/ast/features/transform.js +++ b/dist/lib/ast/features/transform.js @@ -6,7 +6,11 @@ import { FeatureWalkMode } from './type.js'; import { STATE } from '../../syntax/constants.js'; class TransformCssFeature { - accept = new Set([EnumToken.RuleNodeType, EnumToken.KeyframesRuleNodeType]); + accept = new Set([ + EnumToken.RuleNodeType, + EnumToken.AtRuleNodeType, + EnumToken.KeyframesRuleNodeType, + ]); get ordering() { return 3; } @@ -43,7 +47,6 @@ class TransformCssFeature { ? minifyTransformFunctions(child) : child); } - // consumeWhitespace(children); let { matrix, cumulative, minified } = compute(children) ?? { matrix: null, cumulative: null, diff --git a/dist/lib/ast/minify.js b/dist/lib/ast/minify.js index 11b2dac5..0cada89c 100644 --- a/dist/lib/ast/minify.js +++ b/dist/lib/ast/minify.js @@ -37,10 +37,8 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent, co let postprocess = false; let parents; let replacement; - // @ts-ignore let { sourcemap, module, ...options2 } = options; - if (!("features" in options2)) { - // @ts-ignore + if (!(options2.features != null)) { options2 = { removeDuplicateDeclarations: true, computeShorthand: true, @@ -90,10 +88,9 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent, co parent[PARENT] != null) { replaceNodeOrValue(parent[PARENT], parent, replacement); } - if ("chi" in replacement) { - // @ts-ignore + if (replacement.chi != null) { for (const node of replacement.chi) { - // node[PARENT] = replacement; + node[PARENT] = replacement; parents.add(node); } } @@ -131,9 +128,9 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent, co // @ts-ignore replaceNodeOrValue(parent[PARENT], parent, replacement); } - if ("chi" in replacement) { + if (replacement.chi != null) { for (const node of replacement.chi) { - // node[PARENT] = replacement; + node[PARENT] = replacement; parents.add(node); } } diff --git a/dist/lib/parser/parse.js b/dist/lib/parser/parse.js index 642a7b1d..31bb400d 100644 --- a/dist/lib/parser/parse.js +++ b/dist/lib/parser/parse.js @@ -48,7 +48,6 @@ const BadTokensTypes = [ EnumToken.BadStringTokenType, ]; let keyNameCounter = 0; -const forbiddenStartCharacters = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"].map((c) => c.charCodeAt(0)); /** * Short-scoped name generator. * @@ -59,12 +58,15 @@ const forbiddenStartCharacters = ["0", "1", "2", "3", "4", "5", "6", "7", "8", " * * @returns string */ -const getShortNameGenerator = memoize((localName, filePath, pattern, hashLength = 5) => { +const getShortNameGenerator = memoize(() => { let value = keyNameCounter.toString(36); + let val = value.charAt(0).charCodeAt(0); keyNameCounter++; - while (forbiddenStartCharacters.includes(value.charCodeAt(0))) { + // starts with'0' - '9' + while (48 <= val && val <= 57) { value = keyNameCounter.toString(36); keyNameCounter++; + val = value.charAt(0).charCodeAt(0); } return value; }); @@ -312,19 +314,19 @@ function parseVisitors(visitorsDef, errors) { key = visitors[i][0]; value = visitors[i][1]; if (Number.isInteger(+key)) { - if (Array.isArray(value)) { - visitors.splice(i + 1, 0, ...Object.entries(value)); - continue; - } + // if (Array.isArray(value)) { + // visitors.splice(i + 1, 0, ...Object.entries(value)); + // continue; + // } if (typeof value == "function") { key = value.name; } } - if (Array.isArray(value)) { - // @ts-ignore - visitors.splice(i + 1, 0, ...value.map((item) => [key, item])); - continue; - } + // if (Array.isArray(value)) { + // // @ts-ignore + // visitors.splice(i + 1, 0, ...value.map((item) => [key, item])); + // continue; + // } if (key in EnumToken) { if (typeof value == "function") { if (!valuesHandlers.has(EnumToken[key])) { @@ -332,18 +334,27 @@ function parseVisitors(visitorsDef, errors) { } valuesHandlers.get(EnumToken[key]).push(value); } - else if (typeof value == "object" && "type" in value && "handler" in value && value.type in WalkerEvent) { - if (value.type == WalkerEvent.Enter) { - if (!preValuesHandlers.has(EnumToken[key])) { - preValuesHandlers.set(EnumToken[key], []); + else if (typeof value == "object") { + if ("type" in value && "handler" in value && value.type in WalkerEvent) { + if (value.type == WalkerEvent.Enter) { + if (!preValuesHandlers.has(EnumToken[key])) { + preValuesHandlers.set(EnumToken[key], []); + } + preValuesHandlers + .get(EnumToken[key]) + .push(value.handler); } - preValuesHandlers.get(EnumToken[key]).push(value.handler); - } - else if (value.type == WalkerEvent.Leave) { - if (!postValuesHandlers.has(EnumToken[key])) { - postValuesHandlers.set(EnumToken[key], []); + else if (value.type == WalkerEvent.Leave) { + if (!postValuesHandlers.has(EnumToken[key])) { + postValuesHandlers.set(EnumToken[key], []); + } + postValuesHandlers + .get(EnumToken[key]) + .push(value.handler); } - postValuesHandlers.get(EnumToken[key]).push(value.handler); + } + else { + visitors.push(...Object.entries(value)); } } else { @@ -360,6 +371,7 @@ function parseVisitors(visitorsDef, errors) { .push(value); } else if (typeof value == "object") { + // visitors.push(...Object.entries(value)); if ("type" in value && "handler" in value && value.type in WalkerEvent) { if (value.type == WalkerEvent.Enter) { if (!preVisitorsHandlersMap.has(key)) { @@ -496,10 +508,6 @@ function doParseSync(iter, options = {}) { end: 0, srcId: options.source.id, }; - // if (Array.isArray(iter)) { - // // @ts-expect-error - // iter = iter[Symbol.iterator]() as Iterator; - // } for (currentItemIndex = 0; currentItemIndex < iter.length; currentItemIndex++) { item = iter[currentItemIndex]; stats.bytesIn = item.bytesIn; @@ -672,21 +680,20 @@ function doParseSync(iter, options = {}) { if (typeof handler == "function") { handlers.push(handler); } - else if (Array.isArray(handler)) { - for (const h of handler) { - if (typeof h == "function") { - handlers.push(h); - } - // @ts-ignore - else if (h[keyName] != null) { - // @ts-ignore - handlers.push(h[keyName]); - } - } - } - else if (typeof handler.handler == "function") { - handlers.push(handler.handler); - } + // else if (Array.isArray(handler)) { + // for (const h of handler) { + // if (typeof h == "function") { + // handlers.push(h); + // } + // // @ts-ignore + // else if (h[keyName] != null) { + // // @ts-ignore + // handlers.push(h[keyName]); + // } + // } + // } else if (typeof handler.handler! == "function") { + // handlers.push(handler.handler); + // } // @ts-ignore else if (typeof handler[keyName] == "function") { // @ts-ignore @@ -1396,8 +1403,6 @@ async function doParse(iter, options = {}) { curlyBracketMatch--; } tokens.push(item.token); - // console.debug([item.token, {parensMatch, curlyBracketMatch}]); - // if (parensMatch === 0) { if (parensMatch === 0 && (item.token.typ === EnumToken.SemiColonTokenType || item.token.typ === EnumToken.BlockStartTokenType || @@ -1594,21 +1599,20 @@ async function doParse(iter, options = {}) { if (typeof handler == "function") { handlers.push(handler); } - else if (Array.isArray(handler)) { - for (const h of handler) { - if (typeof h == "function") { - handlers.push(h); - } - // @ts-ignore - else if (h[keyName] != null) { - // @ts-ignore - handlers.push(h[keyName]); - } - } - } - else if (typeof handler.handler == "function") { - handlers.push(handler.handler); - } + // else if (Array.isArray(handler)) { + // for (const h of handler) { + // if (typeof h == "function") { + // handlers.push(h); + // } + // // @ts-ignore + // else if (h[keyName] != null) { + // // @ts-ignore + // handlers.push(h[keyName]); + // } + // } + // } else if (typeof handler.handler! == "function") { + // handlers.push(handler.handler); + // } // @ts-ignore else if (typeof handler[keyName] == "function") { // @ts-ignore diff --git a/dist/lib/parser/tokenize.js b/dist/lib/parser/tokenize.js index 2145962b..8a8593e0 100644 --- a/dist/lib/parser/tokenize.js +++ b/dist/lib/parser/tokenize.js @@ -1,6 +1,6 @@ import { EnumToken, ColorType } from '../ast/types.js'; import { LOC, wildCardFuncs, whenElseFunc, transformFunctions, mathFuncs, colorsFunc, timingFunc, supportFunc, timelineFunc, imageFunc, gridTemplateFunc, urlFunc, containerFunc, pseudoElements } from '../syntax/constants.js'; -import { isDigit, isPseudo, isIdent, isWhiteSpace, isNumber, isHexColor, isHash, isPercentage, parseDimension, isNewLine } from '../syntax/syntax.js'; +import { isDigit, isPseudo, isIdent, isWhiteSpace, isURLToken, isNumber, isHexColor, isHash, isPercentage, parseDimension, isNewLine } from '../syntax/syntax.js'; import { SourceFile } from './source.js'; import { equalsIgnoreCase } from './utils/text.js'; @@ -463,59 +463,43 @@ function tokenize(parseInfo, yieldEOFToken = true) { buffer = ""; value = peek(parseInfo); // consume an - while (isWhiteSpace((charCode = value.charCodeAt(0)))) { - buffer += next(parseInfo); - value = peek(parseInfo); - charCode = value.charCodeAt(0); - if (value === "/" && match(parseInfo, "/*")) { - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; - } - buffer += next(parseInfo, 2); - while ((value = next(parseInfo))) { - if (value == "*") { - buffer += value; - if (match(parseInfo, "/")) { - result.push(yieldResult(buffer + next(parseInfo), parseInfo, EnumToken.CommentTokenType)); - buffer = ""; - break; - } - } - else { - buffer += value; - } - } - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo, EnumToken.BadCommentTokenType)); - buffer = ""; - } + while (isWhiteSpace(peek(parseInfo).charCodeAt(0))) { + // buffer += next(parseInfo); + next(parseInfo); + // charCode = value.charCodeAt(0); + } + value = peek(parseInfo); + let values = null; + if (value == '"' || value == "'") { + values = consumeString(parseInfo); + } + else { + do { + buffer += next(parseInfo); value = peek(parseInfo); charCode = value.charCodeAt(0); - } - } - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo, EnumToken.WhitespaceTokenType)); - buffer = ""; + } while ( + // !(value === "/" && match(parseInfo, "/*") && + value !== ")" && + value !== ""); } - if (value === ")" || value === '"' || value === "'") { - break; + if (values) { + if (peek(parseInfo) === "") { + for (let i = 0; i < values.length; i++) { + values[i].token.typ = EnumToken.BadUrlTokenType; + } + } + result.push(...values); } - do { - buffer += next(parseInfo); - value = peek(parseInfo); - charCode = value.charCodeAt(0); - } while (value !== ")" && - !isWhiteSpace(charCode) && - !(value === "/" && match(parseInfo, "/*"))); - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo, peek(parseInfo) === "" + else if (buffer.length > 0) { + result.push(yieldResult(buffer.trimEnd(), parseInfo, + // buffer.length > 0 + peek(parseInfo) === "" || !isURLToken(buffer) ? EnumToken.BadUrlTokenType : EnumToken.UrlTokenTokenType)); buffer = ""; } } - // console.debug({value: peek(parseInfo)}); break; } } @@ -760,7 +744,7 @@ function tokenize(parseInfo, yieldEOFToken = true) { case 92 /* TokenMap.REVERSE_SOLIDUS */: next(parseInfo); // EOF - if (!(peek(parseInfo))) { + if (!peek(parseInfo)) { // end of stream ignore \\ if (buffer.length > 0) { result.push(yieldResult(buffer, parseInfo)); diff --git a/dist/lib/parser/utils/selector.js b/dist/lib/parser/utils/selector.js index 04b911a1..4c4d0b80 100644 --- a/dist/lib/parser/utils/selector.js +++ b/dist/lib/parser/utils/selector.js @@ -1,6 +1,6 @@ import { EnumToken, EnumAstNodeStatus } from '../../ast/types.js'; import { renderValue } from '../../renderer/render.js'; -import { LOC, ERRORS, STATE, TOKENS, pseudoElements, tokensfuncDefMap, combinators, PARENT } from '../../syntax/constants.js'; +import { LOC, ERRORS, STATE, TOKENS, pseudoElements, combinators, tokensfuncDefMap, PARENT } from '../../syntax/constants.js'; import { isHash } from '../../syntax/syntax.js'; import { getParsedSyntax, getSyntaxRule, getSyntaxConfig } from '../../validation/config.js'; import { matchAllSyntaxes, createValidationContext, trimArray, matchSelectorSyntax } from '../../validation/match.js'; @@ -58,7 +58,7 @@ function parseSelector(tokens, context, options, errors) { chi: [], [LOC]: { ...tokens[0][LOC], - end: tokens[tokens.length - 1]?.[LOC]?.end ?? tokens[0]?.[LOC]?.end + end: tokens[tokens.length - 1]?.[LOC]?.end ?? tokens[0]?.[LOC]?.end, }, [TOKENS]: tokens.length === 0 ? null : tokens, [STATE]: result.success ? EnumAstNodeStatus.Validated : EnumAstNodeStatus.Invalid, @@ -151,16 +151,70 @@ function parseSelector(tokens, context, options, errors) { } } if (tokens[i].typ == EnumToken.ColorTokenType) { - // if (isIdent((tokens[i] as ColorToken).val)) { - // Object.assign(tokens[i], { - // typ: EnumToken.IdenTokenType, - // }); - // } else if (isHash(tokens[i].val)) { Object.assign(tokens[i], { typ: EnumToken.HashTokenType, }); } + else { + return { + typ: EnumToken.RuleNodeType, + sel: [ + ...tokens + .reduce((acc, curr, index, array) => { + // if (curr.typ == EnumToken.CommentTokenType) { + // return acc; + // } + if (curr.typ == EnumToken.WhitespaceTokenType) { + if (trimWhiteSpace.includes(array[index - 1]?.typ) || + trimWhiteSpace.includes(array[index + 1]?.typ) || + combinators.includes(array[index - 1]?.val) || + combinators.includes(array[index + 1]?.val)) { + return acc; + } + } + let t = renderValue(curr, { minify: false }); + if (t == ",") { + acc.push([]); + } + else { + acc[acc.length - 1].push(t); + } + return acc; + }, [[]]) + .reduce((acc, curr) => { + let i = 0; + for (; i < curr.length; i++) { + if (i + 1 < curr.length && curr[i] == "*") { + if (curr[i] == "*") { + let index = curr[i + 1] == " " ? 2 : 1; + if (![">", "~", "+"].includes(curr[index])) { + curr.splice(i, index); + } + } + } + } + acc.set(curr.join(""), curr); + return acc; + }, uniq) + .keys(), + ].join(","), + chi: [], + [LOC]: { + ...tokens[0][LOC], + end: tokens[tokens.length - 1][LOC].end, + }, + [TOKENS]: tokens, + [STATE]: EnumAstNodeStatus.Invalid, + [ERRORS]: [ + { + action: "drop", + node: tokens[i], + message: "invalid hash id", + }, + ], + }; + } } } const result = matchSelectorSyntax(tokens, errors, options, nested === true); @@ -301,7 +355,7 @@ function parseSelector(tokens, context, options, errors) { // } else { // Object.assign(token, { typ: EnumToken.NumberTokenType, val: b1 }); // } - // } else + // } else if (b1 === 0) { Object.assign(token, Math.abs(a1) === 1 ? { @@ -372,7 +426,7 @@ function parseSelector(tokens, context, options, errors) { // func.chi.splice(0, i); // } // break; - // } else + // } else if (num.val === 0) { func.chi.splice(index + 1, i - index); if (token.val < 0) { diff --git a/dist/lib/renderer/render.js b/dist/lib/renderer/render.js index 6c90b117..de03e003 100644 --- a/dist/lib/renderer/render.js +++ b/dist/lib/renderer/render.js @@ -61,7 +61,7 @@ function doRender(data, options = {}, mapping) { const startTime = performance.now(); const errors = []; const sourcemap = options.sourcemap ? new SourceMap() : null; - const sourcemaps = options.sourcemap ? [] : null; + const sourcemaps = options.sourcemap ? { sources: [], maps: [] } : null; const cache = Object.create(null); const sourceLocation = { end: 0, @@ -109,7 +109,12 @@ function doRender(data, options = {}, mapping) { }, }; if (sourcemap != null) { - sourcemap.addAll(sourcemaps); + let source; + for (const sourceId of sourcemaps.sources) { + source = options.sourcesMap.get(sourceId); + sourcemap.addSourceContent(source.id, source.getFileName(), source.getContent()); + } + sourcemap.addAll(sourcemaps.maps); result.map = sourcemap; if (options.sourcemap === "inline") { result.code += `\n/*# sourceMappingURL=${result.map.toUrl()} */`; @@ -159,7 +164,7 @@ function updateSourceMap(node, options, cache, sourcemaps, sourceLocation, lines let records = null; let srcId = node[LOC].srcId; let sourceFileName = source.getFileName() || null; - let sourceContent = source.getContent() || null; + source.getContent() || null; if (inputSourceMap != null && (records = inputSourceMap.find(offsets[0], offsets[1])) != null) { for (const record of records) { // @ts-ignore @@ -168,7 +173,6 @@ function updateSourceMap(node, options, cache, sourcemaps, sourceLocation, lines offsets[0] = record[1]; // @ts-ignore offsets[1] = record[2]; - sourceContent = record[3] || null; if (sourceFileName != null && options.output != null && !sourceFileName.startsWith("data:")) { if (cache[sourceFileName] == null) { const absolute = options.resolve(dirname(options.output), options.cwd) @@ -181,7 +185,10 @@ function updateSourceMap(node, options, cache, sourcemaps, sourceLocation, lines } sourceFileName = cache[sourceFileName]; } - sourcemaps.push([newLine, newColumn, srcId, ...offsets, sourceFileName, sourceContent]); + if (!sourcemaps.sources.includes(srcId)) { + sourcemaps.sources.push(srcId); + } + sourcemaps.maps.push([newLine, newColumn, srcId, ...offsets]); } } else { @@ -195,7 +202,10 @@ function updateSourceMap(node, options, cache, sourcemaps, sourceLocation, lines } sourceFileName = cache[sourceFileName]; } - sourcemaps.push([newLine, newColumn, srcId, ...offsets, sourceFileName, sourceContent]); + if (!sourcemaps.sources.includes(srcId)) { + sourcemaps.sources.push(srcId); + } + sourcemaps.maps.push([newLine, newColumn, srcId, ...offsets]); } } move(sourceLocation, linesMap, offset > 0 ? str.slice(offset) : str); @@ -342,12 +352,13 @@ function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, erro // } // } const source = options.sourcesMap.get(node[LOC].srcId); - sourcemaps.push([ + if (!sourcemaps.sources.includes(node[LOC].srcId)) { + sourcemaps.sources.push(node[LOC].srcId); + } + sourcemaps.maps.push([ ...linesMap.getOffsets(sourceLocation.end - str.length + options.newLine.length + indentSub.length), node[LOC].srcId, ...source.getOffsets(node[LOC].sta), - source.getFileName(), - source.getContent(), ]); } } @@ -452,8 +463,8 @@ function renderValue(token, options = {}, cache = Object.create(null), reducer, return " + "; case EnumToken.Sub: return " - "; - case EnumToken.Star: case EnumToken.UniversalSelectorTokenType: + case EnumToken.Star: case EnumToken.Mul: return "*"; case EnumToken.Div: @@ -1292,11 +1303,11 @@ function renderValue(token, options = {}, cache = Object.create(null), reducer, case EnumToken.OrTokenType: return "or"; case EnumToken.InvalidMediaQueryTokenType: - // case EnumToken.InvalidDeclarationNodeType: case EnumToken.InvalidCommentTokenType: case EnumToken.BadCommentTokenType: case EnumToken.BadCdoTokenType: case EnumToken.BadStringTokenType: + case EnumToken.BadUrlTokenType: case EnumToken.EOFTokenType: return ""; default: diff --git a/dist/lib/renderer/sourcemap/sourcemap.js b/dist/lib/renderer/sourcemap/sourcemap.js index ba38bf05..8d63f375 100644 --- a/dist/lib/renderer/sourcemap/sourcemap.js +++ b/dist/lib/renderer/sourcemap/sourcemap.js @@ -76,37 +76,47 @@ class SourceMap { this.computePositions(); } } + hasSourceContent(id) { + return this.sourcesMap.includes(id); + } + addSourceContent(id, fileName, content) { + if (this.sourcesMap.includes(id)) { + return; + } + this.sourcesMap[this.sourcesMap.length] = id; + this.sources[this.sources.length] = fileName; + this.sourcesContent[this.sourcesContent.length] = content; + } /** * Add all location * @param maps */ addAll(maps) { - for (let [newLine, newColumn, srcId, ln, col, sourceFileName, sourceContent] of maps) { - const key = `${srcId}:${ln}:${sourceFileName}:${col}:${newLine}:${newColumn}:${sourceContent}`; - const sourcemap = `${srcId}:${sourceFileName}:${sourceContent}`; + let srcIndex; + for (let [newLine, newColumn, srcId, ln, col] of maps) { + const key = `${srcId}:${ln}:${col}:${newLine}:${newColumn}`; if (this.keys.has(key)) { continue; } this.keys.add(key); - if (!this.sourcesMap.includes(sourcemap)) { - this.sourcesMap.push(sourcemap); - this.sources.push(sourceFileName || null); - this.sourcesContent.push((sourceFileName != null ? null : sourceContent) || null); - } const line = newLine - 1; let record; if (line > this.line) { this.line = line; } + srcIndex = this.sourcesMap.indexOf(srcId); + if (srcIndex == -1) { + throw new Error(`Source file ${srcId} not added to sourcemap`); + } if (!this.map.has(line)) { - record = [Math.max(0, newColumn - 1), this.sourcesMap.indexOf(sourcemap), ln - 1, col - 1]; + record = [Math.max(0, newColumn - 1), srcIndex, ln - 1, col - 1]; this.map.set(line, [record]); } else { const arr = this.map.get(line); record = [ Math.max(0, newColumn - 1) - arr[0][0], - this.sourcesMap.indexOf(sourcemap) - arr[0][1], + srcIndex - arr[0][1], ln - 1, col - 1, ]; diff --git a/dist/lib/syntax/syntax.js b/dist/lib/syntax/syntax.js index 726c3421..37ab53c3 100644 --- a/dist/lib/syntax/syntax.js +++ b/dist/lib/syntax/syntax.js @@ -967,13 +967,51 @@ const isIdent = memoize(function (name) { } return true; }); +function isNonPrintable(codepoint) { + // null -> backspace + return ((codepoint >= 0 && codepoint <= 0x8) || + // tab + codepoint == 0xb || + // delete + codepoint == 0x7f || + (codepoint >= 0xe && codepoint <= 0x1f)); +} +function isURLToken(str) { + let i = -1; + let c; + while (++i < str.length) { + c = str.charCodeAt(i); + // single quote or double quote or start parenthesis or close parenthesis + if (isNonPrintable(c) || c == 0x27 || c == 0x22 || c == 0x28 || c == 0x29) { + return false; + } + // valid escape + if (c == REVERSE_SOLIDUS) { + i++; + if (i >= str.length) { + return false; + } + c = str.charCodeAt(i); + // c is not '\n' or '\r' or '\f' + if (c == 0x6e || c == 0x72 || c == 0x66) { + return false; + } + continue; + } + // is white space + if (c == 0x20 || c == 0x09) { + break; + } + } + return i == str.length; +} function isPseudo(name) { return (name.charAt(0) == ":" && ((name.endsWith("(") && isIdent(name.charAt(1) == ":" ? name.slice(2, -1) : name.slice(1, -1))) || isIdent(name.charAt(1) == ":" ? name.slice(2) : name.slice(1)))); } function isHash(name) { - return name.charAt(0) == "#" && isIdent(name.charAt(1)); + return name.charAt(0) == "#" && isIdentStart(name.charCodeAt(1)); } const isNumber = memoize(function (name) { // if (name.length == 0) { @@ -1225,4 +1263,4 @@ function toPrecisionAngle(angle, precision = colorPrecision, correctValue = true return angle; } -export { dimensionUnits, isAngle, isColor, isDigit, isFlex, isFrequency, isFunction, isHash, isHexColor, isIdent, isIdentCodepoint, isIdentColor, isIdentStart, isLength, isLetter, isNewLine, isNumber, isPercentage, isPolarColorspace, isPseudo, isRectangularOrthogonalColorspace, isResolution, isTime, isWhiteSpace, length2Px, minifyNumber, parseColor, parseDimension, pseudoAliasMap, reduceColorStops, reduceConicColorStops, reducegradientBackgroundPosition, renamedStandardProperties, toPrecisionAngle, toPrecisionValue }; +export { dimensionUnits, isAngle, isColor, isDigit, isFlex, isFrequency, isFunction, isHash, isHexColor, isIdent, isIdentCodepoint, isIdentColor, isIdentStart, isLength, isLetter, isNewLine, isNonPrintable, isNumber, isPercentage, isPolarColorspace, isPseudo, isRectangularOrthogonalColorspace, isResolution, isTime, isURLToken, isWhiteSpace, length2Px, minifyNumber, parseColor, parseDimension, pseudoAliasMap, reduceColorStops, reduceConicColorStops, reducegradientBackgroundPosition, renamedStandardProperties, toPrecisionAngle, toPrecisionValue }; diff --git a/dist/utils/sync.js b/dist/utils/sync.js index d8b3dff7..6d8f258f 100644 --- a/dist/utils/sync.js +++ b/dist/utils/sync.js @@ -50,12 +50,12 @@ function validateSyncArguments(options, prefix = "options.") { let i; for (i = 0; i < args.length; i++) { const [key, value] = args[i]; - if (typeof value == 'function') { + if (typeof value == "function") { if (value instanceof Promise || Object.getPrototypeOf(value).constructor.name == "AsyncFunction") { throw new Error(`[${prefix + key}]: Async functions are not supported in sync mode. Use parse() or transform() instead.`); } } - else if (value != null && typeof value == 'object') { + else if (value != null && typeof value == "object") { validateSyncArguments(value, prefix + key + "."); } } diff --git a/src/lib/ast/expand.ts b/src/lib/ast/expand.ts index b2687841..015d1382 100644 --- a/src/lib/ast/expand.ts +++ b/src/lib/ast/expand.ts @@ -1,10 +1,11 @@ import { splitRule } from "./minify.ts"; -import { combinators, PARENT, RAW } from "../syntax/constants.ts"; +import { combinators, PARENT, RAW, STATE } from "../syntax/constants.ts"; import { parseString } from "../parser/parse.ts"; import { walkValues } from "./walk.ts"; import { renderValue } from "../renderer/render.ts"; import type { AstAtRule, AstNode, AstRule, AstStyleSheet, LiteralToken, Token } from "../../@types/index.d.ts"; -import { EnumToken } from "./types.ts"; +import { EnumAstNodeStatus, EnumToken } from "./types.ts"; +import { cloneNode } from "./clone.ts"; /** * expand css nesting ast nodes @@ -13,7 +14,18 @@ import { EnumToken } from "./types.ts"; * @private */ export function expand(ast: AstStyleSheet | AstAtRule | AstRule): AstNode { - const result = { ...ast, chi: [] }; + + if( + (ast as AstNode)[STATE] == EnumAstNodeStatus.Invalid || + (ast as AstNode)[STATE] == EnumAstNodeStatus.Disallowed || + (ast as AstNode)[STATE] == EnumAstNodeStatus.Unknown || + (ast as AstNode)[STATE] == EnumAstNodeStatus.Unparsed || + (ast as AstNode)[STATE] == EnumAstNodeStatus.Malformed + ) { + return ast; + } + + const result = Object.assign(cloneNode(ast), { chi: [] }) as AstStyleSheet | AstAtRule; let children: AstNode[]; for (let i = 0; i < ast.chi!.length; i++) { @@ -67,7 +79,18 @@ export function expand(ast: AstStyleSheet | AstAtRule | AstRule): AstNode { } function expandRule(node: AstRule): Array { - const ast: AstRule = { ...node, chi: node.chi.slice() }; + + if( + (node as AstNode)[STATE] == EnumAstNodeStatus.Invalid || + (node as AstNode)[STATE] == EnumAstNodeStatus.Disallowed || + (node as AstNode)[STATE] == EnumAstNodeStatus.Unknown || + (node as AstNode)[STATE] == EnumAstNodeStatus.Unparsed || + (node as AstNode)[STATE] == EnumAstNodeStatus.Malformed + ) { + return [node]; + } + + const ast: AstRule = Object.assign(cloneNode(node), {chi: node.chi.slice() }) as AstRule; const result: Array = []; if (ast.typ == EnumToken.RuleNodeType) { diff --git a/src/lib/ast/features/prefix.ts b/src/lib/ast/features/prefix.ts index 12450329..debe40d6 100644 --- a/src/lib/ast/features/prefix.ts +++ b/src/lib/ast/features/prefix.ts @@ -90,17 +90,19 @@ function replaceAstNodes(tokens: Token[], root?: AstNode): boolean { const isPseudClass: boolean = pseudoAliasMap[key].startsWith("::"); (value as PseudoClassToken).val = pseudoAliasMap[key]; - if ( - value.typ == EnumToken.IdenTokenType && - ["min-resolution", "max-resolution"].includes((value as IdentToken).val) && - parent?.typ == EnumToken.MediaQueryConditionTokenType && - (parent as MediaQueryConditionToken).r?.[0]?.typ == EnumToken.NumberTokenType - ) { - Object.assign((parent as MediaQueryConditionToken).r?.[0], { - typ: EnumToken.ResolutionTokenType, - unit: "x", - }); - } else if (isPseudClass && value.typ == EnumToken.PseudoElementTokenType) { + // if ( + // value.typ == EnumToken.IdenTokenType && + // ["min-resolution", "max-resolution"].includes((value as IdentToken).val) && + // parent?.typ == EnumToken.MediaQueryConditionTokenType && + // (parent as MediaQueryConditionToken).r?.[0]?.typ == EnumToken.NumberTokenType + // ) { + // Object.assign((parent as MediaQueryConditionToken).r?.[0], { + // typ: EnumToken.ResolutionTokenType, + // unit: "x", + // }); + // } + // else + if (isPseudClass && value.typ == EnumToken.PseudoElementTokenType) { // @ts-ignore value.typ = EnumToken.PseudoClassTokenType; } @@ -386,36 +388,11 @@ export class ComputePrefixFeature { let type: string = ""; let tokens = token.chi.slice(); - // while ( - // i < tokens.length && - // (tokens[i].typ === EnumToken.WhitespaceTokenType || tokens[i].typ === EnumToken.CommentTokenType) - // ) { - // i++; - // } - - // if (i >= tokens.length || tokens[i].typ !== EnumToken.IdenTokenType) { - // return; - // } - // linear or radial if (equalsIgnoreCase((tokens[i] as IdentToken).val, "linear")) { type = "linear-gradient"; i++; } - // else { - // return; - // } - - // while ( - // i < tokens.length && - // (tokens[i].typ === EnumToken.WhitespaceTokenType || tokens[i].typ === EnumToken.CommentTokenType) - // ) { - // i++; - // } - - // if (tokens[i].typ !== EnumToken.CommaTokenType) { - // return; - // } tokens.splice(0, i + 1); diff --git a/src/lib/ast/features/transform.ts b/src/lib/ast/features/transform.ts index 1c525520..88d5a1bf 100644 --- a/src/lib/ast/features/transform.ts +++ b/src/lib/ast/features/transform.ts @@ -15,7 +15,11 @@ import { FeatureWalkMode } from "./type.ts"; import { STATE } from "../../syntax/constants.ts"; export class TransformCssFeature { - public accept: Set = new Set([EnumToken.RuleNodeType, EnumToken.KeyframesRuleNodeType]); + public accept: Set = new Set([ + EnumToken.RuleNodeType, + EnumToken.AtRuleNodeType, + EnumToken.KeyframesRuleNodeType, + ]); get ordering(): number { return 3; @@ -66,8 +70,6 @@ export class TransformCssFeature { ); } - // consumeWhitespace(children); - let { matrix, cumulative, minified } = compute(children as Token[]) ?? { matrix: null, cumulative: null, diff --git a/src/lib/ast/minify.ts b/src/lib/ast/minify.ts index 00b4acd3..3e1f51b7 100644 --- a/src/lib/ast/minify.ts +++ b/src/lib/ast/minify.ts @@ -1,7 +1,7 @@ -import {eq} from "../parser/utils/eq.ts"; -import {doRender, renderValue} from "../renderer/render.ts"; +import { eq } from "../parser/utils/eq.ts"; +import { doRender, renderValue } from "../renderer/render.ts"; import * as allFeatures from "./features/index.ts"; -import {walkValues} from "./walk.ts"; +import { walkValues } from "./walk.ts"; import type { AstAtRule, AstDeclaration, @@ -24,15 +24,15 @@ import type { RawSelectorTokens, Token, } from "../../@types/index.d.ts"; -import {EnumToken} from "./types.ts"; -import {isFunction, isIdent, isIdentStart, isWhiteSpace} from "../syntax/syntax.ts"; -import {FeatureWalkMode} from "./features/type.ts"; -import {trimArray} from "../validation/match.ts"; -import {combinators, LOC, OPTIMIZED, PARENT, RAW, TOKENS} from "../syntax/constants.ts"; -import {replaceNodeOrValue} from "../parser/utils/token.ts"; -import {parseString} from "../parser/parse.ts"; -import {tokenize} from "../parser/tokenize.ts"; -import {replaceCompound} from "./expand.ts"; +import { EnumToken } from "./types.ts"; +import { isFunction, isIdent, isIdentStart, isWhiteSpace } from "../syntax/syntax.ts"; +import { FeatureWalkMode } from "./features/type.ts"; +import { trimArray } from "../validation/match.ts"; +import { combinators, LOC, OPTIMIZED, PARENT, RAW, TOKENS } from "../syntax/constants.ts"; +import { replaceNodeOrValue } from "../parser/utils/token.ts"; +import { parseString } from "../parser/parse.ts"; +import { tokenize } from "../parser/tokenize.ts"; +import { replaceCompound } from "./expand.ts"; const notEndingWith: string[] = ["(", "["].concat(combinators); const rules: EnumToken[] = [ @@ -90,19 +90,17 @@ export function minify( let parents: Set; let replacement: AstNode | null; - // @ts-ignore - let { sourcemap, module, ...options2 } = options; + let { sourcemap, module, ...options2 } = options as ParserOptions; - if (!("features" in options2)) { - // @ts-ignore - options2 = { + if (!((options2 as MinifyFeatureOptions).features != null)) { + options2 = { removeDuplicateDeclarations: true, computeShorthand: true, computeCalcExpression: true, removePrefix: false, - features: [], + features: [] as Function[], ...options2, - }; + } as MinifyFeatureOptions; for (const feature of features) { feature.register(options2); @@ -148,9 +146,9 @@ export function minify( } const result = feature.run( - replacement, + replacement as AstRule | AstAtRule, options2, - parent[PARENT] ?? ast, + parent[PARENT] ?? (ast as AstRule | AstAtRule | AstStyleSheet), context, FeatureWalkMode.Pre, ); @@ -169,10 +167,9 @@ export function minify( replaceNodeOrValue(parent[PARENT] as AstRule | AstAtRule | AstStyleSheet, parent, replacement); } - if ("chi" in replacement) { - // @ts-ignore + if (replacement.chi != null) { for (const node of replacement.chi) { - // node[PARENT] = replacement; + node[PARENT] = replacement; parents.add(node as AstNode); } } @@ -181,13 +178,12 @@ export function minify( for (const feature of options2.features as MinifyFeature[]) { if (feature.processMode & FeatureWalkMode.Pre && "cleanup" in feature) { // @ts-ignore - feature.cleanup(ast, options2, context, FeatureWalkMode.Pre); + feature.cleanup(ast as AstStyleSheet, options2, context, FeatureWalkMode.Pre); } } } doMinify(ast, options2, recursive, errors, nestingContent, context); - parents = new Set([ast]); for (const parent of parents) { @@ -230,9 +226,9 @@ export function minify( replaceNodeOrValue(parent[PARENT], parent, replacement); } - if ("chi" in replacement) { + if (replacement.chi != null) { for (const node of replacement.chi!) { - // node[PARENT] = replacement; + node[PARENT] = replacement; parents.add(node as AstNode); } } @@ -242,7 +238,7 @@ export function minify( for (const feature of options2.features as MinifyFeature[]) { if (feature.processMode & FeatureWalkMode.Post && "cleanup" in feature) { // @ts-ignore - feature.cleanup(ast, options2, context, FeatureWalkMode.Post); + feature.cleanup(ast as AstStyleSheet, options2, context, FeatureWalkMode.Post); } } } diff --git a/src/lib/parser/parse.ts b/src/lib/parser/parse.ts index c8f5d95f..9ed4dfc5 100644 --- a/src/lib/parser/parse.ts +++ b/src/lib/parser/parse.ts @@ -94,9 +94,6 @@ const BadTokensTypes: EnumToken[] = [ ]; new Map([["keyframes", EnumToken.KeyframesAtRuleNodeType]]); let keyNameCounter: number = 0; -const forbiddenStartCharacters: number[] = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"].map((c) => - c.charCodeAt(0), -); /** * Short-scoped name generator. @@ -108,19 +105,20 @@ const forbiddenStartCharacters: number[] = ["0", "1", "2", "3", "4", "5", "6", " * * @returns string */ -export const getShortNameGenerator = memoize( - (localName: string, filePath: string, pattern: string, hashLength = 5): string => { - let value: string = keyNameCounter!.toString(36); +export const getShortNameGenerator = memoize((): string => { + let value: string = keyNameCounter!.toString(36); + let val: number = value.charAt(0).charCodeAt(0); + keyNameCounter!++; + + // starts with'0' - '9' + while (48 <= val && val <= 57) { + value = keyNameCounter!.toString(36); keyNameCounter!++; + val = value.charAt(0).charCodeAt(0); + } - while (forbiddenStartCharacters.includes(value.charCodeAt(0))) { - value = keyNameCounter!.toString(36); - keyNameCounter!++; - } - - return value; - }, -); + return value; +}); function reject(reason?: any) { throw new Error(reason ?? "Parsing aborted"); @@ -450,21 +448,21 @@ function parseVisitors( value = visitors[i][1]; if (Number.isInteger(+key)) { - if (Array.isArray(value)) { - visitors.splice(i + 1, 0, ...Object.entries(value)); - continue; - } + // if (Array.isArray(value)) { + // visitors.splice(i + 1, 0, ...Object.entries(value)); + // continue; + // } if (typeof value == "function") { key = value.name; } } - if (Array.isArray(value)) { - // @ts-ignore - visitors.splice(i + 1, 0, ...value.map((item) => [key, item])); - continue; - } + // if (Array.isArray(value)) { + // // @ts-ignore + // visitors.splice(i + 1, 0, ...value.map((item) => [key, item])); + // continue; + // } if (key in EnumToken) { if (typeof value == "function") { @@ -473,19 +471,27 @@ function parseVisitors( } valuesHandlers.get(EnumToken[key as keyof typeof EnumToken] as EnumToken)!.push(value); - } else if (typeof value == "object" && "type" in value && "handler" in value && value.type in WalkerEvent) { - if (value.type == WalkerEvent.Enter) { - if (!preValuesHandlers.has(EnumToken[key as keyof typeof EnumToken] as EnumToken)) { - preValuesHandlers.set(EnumToken[key as keyof typeof EnumToken] as EnumToken, []); - } + } else if (typeof value == "object") { + if ("type" in value && "handler" in value && value.type in WalkerEvent) { + if (value.type == WalkerEvent.Enter) { + if (!preValuesHandlers.has(EnumToken[key as keyof typeof EnumToken] as EnumToken)) { + preValuesHandlers.set(EnumToken[key as keyof typeof EnumToken] as EnumToken, []); + } - preValuesHandlers.get(EnumToken[key as keyof typeof EnumToken] as EnumToken)!.push(value.handler); - } else if (value.type == WalkerEvent.Leave) { - if (!postValuesHandlers.has(EnumToken[key as keyof typeof EnumToken] as EnumToken)) { - postValuesHandlers.set(EnumToken[key as keyof typeof EnumToken] as EnumToken, []); - } + preValuesHandlers + .get(EnumToken[key as keyof typeof EnumToken] as EnumToken)! + .push(value.handler); + } else if (value.type == WalkerEvent.Leave) { + if (!postValuesHandlers.has(EnumToken[key as keyof typeof EnumToken] as EnumToken)) { + postValuesHandlers.set(EnumToken[key as keyof typeof EnumToken] as EnumToken, []); + } - postValuesHandlers.get(EnumToken[key as keyof typeof EnumToken] as EnumToken)!.push(value.handler); + postValuesHandlers + .get(EnumToken[key as keyof typeof EnumToken] as EnumToken)! + .push(value.handler); + } + } else { + visitors.push(...Object.entries(value)); } } else { errors.push({ action: "ignore", message: `doParse: visitor.${key} is not a valid key name` }); @@ -507,6 +513,8 @@ function parseVisitors( .get(key as "Declaration" | "Rule" | "AtRule" | "KeyframesRule" | "KeyframesAtRule")! .push(value); } else if (typeof value == "object") { + // visitors.push(...Object.entries(value)); + if ("type" in value && "handler" in value && value.type in WalkerEvent) { if (value.type == WalkerEvent.Enter) { if ( @@ -700,11 +708,6 @@ export function doParseSync( srcId: options.source!.id, }; - // if (Array.isArray(iter)) { - // // @ts-expect-error - // iter = iter[Symbol.iterator]() as Iterator; - // } - for (currentItemIndex = 0; currentItemIndex < (iter as Array).length; currentItemIndex++) { item = (iter as Array)[currentItemIndex]; stats.bytesIn = item.bytesIn; @@ -912,21 +915,22 @@ export function doParseSync( for (const handler of map!.get(genericKey)!) { if (typeof handler == "function") { handlers.push(handler as GenericVisitorHandler); - } else if (Array.isArray(handler)) { - for (const h of handler) { - if (typeof h == "function") { - handlers.push(h); - } - - // @ts-ignore - else if (h[keyName] != null) { - // @ts-ignore - handlers.push(h[keyName]); - } - } - } else if (typeof handler.handler! == "function") { - handlers.push(handler.handler); } + // else if (Array.isArray(handler)) { + // for (const h of handler) { + // if (typeof h == "function") { + // handlers.push(h); + // } + + // // @ts-ignore + // else if (h[keyName] != null) { + // // @ts-ignore + // handlers.push(h[keyName]); + // } + // } + // } else if (typeof handler.handler! == "function") { + // handlers.push(handler.handler); + // } // @ts-ignore else if (typeof handler[keyName]! == "function") { @@ -1834,9 +1838,6 @@ export async function doParse( tokens.push(item.token); - // console.debug([item.token, {parensMatch, curlyBracketMatch}]); - - // if (parensMatch === 0) { if ( parensMatch === 0 && (item.token.typ === EnumToken.SemiColonTokenType || @@ -2078,22 +2079,24 @@ export async function doParse( for (const handler of map!.get(genericKey)!) { if (typeof handler == "function") { handlers.push(handler as GenericVisitorHandler); - } else if (Array.isArray(handler)) { - for (const h of handler) { - if (typeof h == "function") { - handlers.push(h); - } - - // @ts-ignore - else if (h[keyName] != null) { - // @ts-ignore - handlers.push(h[keyName]); - } - } - } else if (typeof handler.handler! == "function") { - handlers.push(handler.handler); } + // else if (Array.isArray(handler)) { + // for (const h of handler) { + // if (typeof h == "function") { + // handlers.push(h); + // } + + // // @ts-ignore + // else if (h[keyName] != null) { + // // @ts-ignore + // handlers.push(h[keyName]); + // } + // } + // } else if (typeof handler.handler! == "function") { + // handlers.push(handler.handler); + // } + // @ts-ignore else if (typeof handler[keyName]! == "function") { // @ts-ignore diff --git a/src/lib/parser/tokenize.ts b/src/lib/parser/tokenize.ts index 865ecc9e..032df7af 100644 --- a/src/lib/parser/tokenize.ts +++ b/src/lib/parser/tokenize.ts @@ -41,6 +41,7 @@ import { isNumber, isPercentage, isPseudo, + isURLToken, isWhiteSpace, parseDimension, } from "../syntax/syntax.ts"; @@ -453,7 +454,7 @@ export function next(parseInfo: ParseInfo, count: number = 1): string { let codepoint: number; for (; i < char.length; i++) { - codepoint = char[i].charCodeAt(0); + codepoint = char[i].charCodeAt(0); if ( codepoint == 0xa || // \n @@ -578,83 +579,57 @@ export function tokenize(parseInfo: ParseInfo | string, yieldEOFToken: boolean = value = peek(parseInfo); // consume an - while (isWhiteSpace((charCode = value.charCodeAt(0)))) { - buffer += next(parseInfo); - value = peek(parseInfo); - charCode = value.charCodeAt(0); - - if (value === "/" && match(parseInfo, "/*")) { - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; - } + while (isWhiteSpace(peek(parseInfo).charCodeAt(0))) { + // buffer += next(parseInfo); + next(parseInfo); + // charCode = value.charCodeAt(0); + } - buffer += next(parseInfo, 2); - - while ((value = next(parseInfo))) { - if (value == "*") { - buffer += value; - - if (match(parseInfo, "/")) { - result.push( - yieldResult( - buffer + next(parseInfo), - parseInfo, - EnumToken.CommentTokenType, - ), - ); - buffer = ""; - break; - } - } else { - buffer += value; - } - } + value = peek(parseInfo); - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo, EnumToken.BadCommentTokenType)); - buffer = ""; - } + let values: Array | null = null; + if (value == '"' || value == "'") { + values = consumeString(parseInfo); + } else { + do { + buffer += next(parseInfo); value = peek(parseInfo); charCode = value.charCodeAt(0); - } + } while ( + // !(value === "/" && match(parseInfo, "/*") && + value !== ")" && + value !== "" + ); } - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo, EnumToken.WhitespaceTokenType)); - buffer = ""; - } + if (values) { - if (value === ")" || value === '"' || value === "'") { - break; - } + if (peek(parseInfo) === "" ) { - do { - buffer += next(parseInfo); - value = peek(parseInfo); - charCode = value.charCodeAt(0); - } while ( - value !== ")" && - !isWhiteSpace(charCode) && - !(value === "/" && match(parseInfo, "/*")) - ); - - if (buffer.length > 0) { - result.push( - yieldResult( - buffer, - parseInfo, - peek(parseInfo) === "" - ? EnumToken.BadUrlTokenType - : EnumToken.UrlTokenTokenType, - ), - ); - buffer = ""; + for (let i = 0; i < values.length; i++) { + + values[i].token.typ = EnumToken.BadUrlTokenType; + } + } + + result.push(...values); } - } - // console.debug({value: peek(parseInfo)}); + else if (buffer.length > 0) { + result.push( + yieldResult( + buffer.trimEnd(), + parseInfo, + // buffer.length > 0 + peek(parseInfo) === "" || !isURLToken(buffer) + ? EnumToken.BadUrlTokenType + : EnumToken.UrlTokenTokenType, + ), + ); + buffer = ""; + } + } break; } @@ -958,7 +933,7 @@ export function tokenize(parseInfo: ParseInfo | string, yieldEOFToken: boolean = next(parseInfo); // EOF - if (!(peek(parseInfo))) { + if (!peek(parseInfo)) { // end of stream ignore \\ if (buffer.length > 0) { result.push(yieldResult(buffer, parseInfo)); diff --git a/src/lib/parser/utils/selector.ts b/src/lib/parser/utils/selector.ts index 4dfa2340..feadd4ae 100644 --- a/src/lib/parser/utils/selector.ts +++ b/src/lib/parser/utils/selector.ts @@ -20,7 +20,16 @@ import type { } from "../../../@types/index.d.ts"; import { EnumAstNodeStatus, EnumToken } from "../../ast/types.ts"; import { renderValue } from "../../renderer/render.ts"; -import { combinators, ERRORS, LOC, PARENT, pseudoElements, STATE, TOKENS, tokensfuncDefMap } from "../../syntax/constants.ts"; +import { + combinators, + ERRORS, + LOC, + PARENT, + pseudoElements, + STATE, + TOKENS, + tokensfuncDefMap, +} from "../../syntax/constants.ts"; import { isHash } from "../../syntax/syntax.ts"; import { getParsedSyntax, getSyntaxConfig, getSyntaxRule } from "../../validation/config.ts"; import { createValidationContext, matchAllSyntaxes, matchSelectorSyntax, trimArray } from "../../validation/match.ts"; @@ -107,7 +116,7 @@ export function parseSelector( chi: [], [LOC]: { ...tokens[0][LOC], - end: tokens[tokens.length - 1]?.[LOC]?.end ?? tokens[0]?.[LOC]?.end + end: tokens[tokens.length - 1]?.[LOC]?.end ?? tokens[0]?.[LOC]?.end, }, [TOKENS]: tokens.length === 0 ? null : tokens, [STATE]: result.success ? EnumAstNodeStatus.Validated : EnumAstNodeStatus.Invalid, @@ -223,15 +232,80 @@ export function parseSelector( } if (tokens[i].typ == EnumToken.ColorTokenType) { - // if (isIdent((tokens[i] as ColorToken).val)) { - // Object.assign(tokens[i], { - // typ: EnumToken.IdenTokenType, - // }); - // } else - if (isHash((tokens[i] as ColorToken).val)) { + + if (isHash((tokens[i] as ColorToken).val)) { Object.assign(tokens[i], { typ: EnumToken.HashTokenType, }); + } else { + + return { + typ: EnumToken.RuleNodeType, + sel: [ + ...tokens + .reduce( + (acc: string[][], curr: Token, index: number, array: Token[]) => { + // if (curr.typ == EnumToken.CommentTokenType) { + // return acc; + // } + + if (curr.typ == EnumToken.WhitespaceTokenType) { + if ( + trimWhiteSpace.includes(array[index - 1]?.typ) || + trimWhiteSpace.includes(array[index + 1]?.typ) || + combinators.includes((array[index - 1])?.val) || + combinators.includes((array[index + 1])?.val) + ) { + return acc; + } + } + + let t: string = renderValue(curr, { minify: false }); + + if (t == ",") { + acc.push([]); + } else { + acc[acc.length - 1].push(t); + } + return acc; + }, + [[]], + ) + .reduce((acc: Map, curr: string[]) => { + let i: number = 0; + + for (; i < curr.length; i++) { + if (i + 1 < curr.length && curr[i] == "*") { + if (curr[i] == "*") { + let index: number = curr[i + 1] == " " ? 2 : 1; + + if (![">", "~", "+"].includes(curr[index])) { + curr.splice(i, index); + } + } + } + } + + acc.set(curr.join(""), curr); + return acc; + }, uniq) + .keys(), + ].join(","), + chi: [], + [LOC]: { + ...tokens[0][LOC], + end: tokens[tokens.length - 1][LOC]!.end, + }, + [TOKENS]: tokens, + [STATE]: EnumAstNodeStatus.Invalid, + [ERRORS]: [ + { + action: "drop", + node: tokens[i], + message: "invalid hash id", + }, + ], + } as AstRule; } } } @@ -407,8 +481,8 @@ export function parseSelector( // } else { // Object.assign(token, { typ: EnumToken.NumberTokenType, val: b1 }); // } - // } else - if (b1 === 0) { + // } else + if (b1 === 0) { Object.assign( token, Math.abs(a1) === 1 @@ -422,7 +496,7 @@ export function parseSelector( unit: "n", }, ); - } + } // else if (Math.abs(a1) === 2) { // if (b1 === 0) { // Object.assign(token, { @@ -493,8 +567,8 @@ export function parseSelector( // } // break; - // } else - if (num.val === 0) { + // } else + if (num.val === 0) { func.chi.splice(index + 1, i - index); if (((token as DimensionToken).val as number) < 0) { diff --git a/src/lib/renderer/render.ts b/src/lib/renderer/render.ts index ee0d6106..eac2907d 100644 --- a/src/lib/renderer/render.ts +++ b/src/lib/renderer/render.ts @@ -133,8 +133,8 @@ export function doRender( const startTime: number = performance.now(); const errors: ErrorDescription[] = []; const sourcemap: SourceMap | null = options.sourcemap ? new SourceMap() : null; - const sourcemaps: Array<[number, number, number, number, number, string | null, string | null]> | null = - options.sourcemap ? [] : null; + const sourcemaps: { sources: number[]; maps: Array<[number, number, number, number, number]> } | null = + options.sourcemap ? { sources: [], maps: [] } : null; const cache: { [key: string]: any; } = Object.create(null); @@ -222,7 +222,13 @@ export function doRender( }; if (sourcemap != null) { - sourcemap.addAll(sourcemaps!); + let source: SourceFile; + for (const sourceId of sourcemaps!.sources) { + source = options!.sourcesMap!.get(sourceId)! as SourceFile; + sourcemap.addSourceContent(source.id, source.getFileName(), source.getContent()); + } + + sourcemap.addAll(sourcemaps!.maps!); result.map = sourcemap; if (options.sourcemap === "inline") { @@ -251,7 +257,7 @@ function updateSourceMap( cache: { [p: string]: any; }, - sourcemaps: Array<[number, number, number, number, number, string | null, string | null]>, + sourcemaps: { sources: number[]; maps: Array<[number, number, number, number, number]> }, sourceLocation: SourceLocation, linesMap: LinesMap, str: string, @@ -323,7 +329,11 @@ function updateSourceMap( sourceFileName = cache[sourceFileName] as string; } - sourcemaps.push([newLine, newColumn, srcId, ...offsets, sourceFileName as string, sourceContent]); + if (!sourcemaps.sources.includes(srcId)) { + sourcemaps.sources.push(srcId); + } + + sourcemaps.maps.push([newLine, newColumn, srcId, ...offsets]); } } else { if (sourceFileName != null && options.output != null && !sourceFileName.startsWith("data:")) { @@ -339,7 +349,11 @@ function updateSourceMap( sourceFileName = cache[sourceFileName] as string; } - sourcemaps.push([newLine, newColumn, srcId, ...offsets, sourceFileName as string, sourceContent]); + if (!sourcemaps.sources.includes(srcId)) { + sourcemaps.sources.push(srcId); + } + + sourcemaps.maps.push([newLine, newColumn, srcId, ...offsets]); } } @@ -399,7 +413,7 @@ export function move(sourceLocation: SourceLocation, linesMap: LinesMap, str: st function renderAstNode( data: AstNode, options: RenderOptions, - sourcemaps: Array<[number, number, number, number, number, string | null, string | null]> | null, + sourcemaps: { sources: number[]; maps: Array<[number, number, number, number, number]> } | null, sourceLocation: SourceLocation, linesMap: LinesMap | null, errors: ErrorDescription[], @@ -509,7 +523,6 @@ function renderAstNode( ? "" : (node).val; } else if (node.typ == EnumToken.DeclarationNodeType) { - str = `${(node).nam}:${options.indent}${(options.minify ? filterValues((node).val) : (node).val @@ -557,14 +570,17 @@ function renderAstNode( // } // } const source = options.sourcesMap!.get(node[LOC]!.srcId) as SourceFile; - sourcemaps.push([ + + if (!sourcemaps.sources.includes(node[LOC]!.srcId as number)) { + sourcemaps.sources.push(node[LOC]!.srcId as number); + } + + sourcemaps.maps.push([ ...linesMap!.getOffsets( sourceLocation.end - str.length + options.newLine!.length + indentSub.length, ), node[LOC]!.srcId, ...source!.getOffsets(node[LOC].sta), - source.getFileName(), - source.getContent(), ]); } } @@ -718,8 +734,8 @@ export function renderValue( case EnumToken.Sub: return " - "; - case EnumToken.Star: case EnumToken.UniversalSelectorTokenType: + case EnumToken.Star: case EnumToken.Mul: return "*"; @@ -1923,11 +1939,11 @@ export function renderValue( return "or"; case EnumToken.InvalidMediaQueryTokenType: - // case EnumToken.InvalidDeclarationNodeType: case EnumToken.InvalidCommentTokenType: case EnumToken.BadCommentTokenType: case EnumToken.BadCdoTokenType: case EnumToken.BadStringTokenType: + case EnumToken.BadUrlTokenType: case EnumToken.EOFTokenType: return ""; diff --git a/src/lib/renderer/sourcemap/sourcemap.ts b/src/lib/renderer/sourcemap/sourcemap.ts index c6dc31ca..32149d20 100644 --- a/src/lib/renderer/sourcemap/sourcemap.ts +++ b/src/lib/renderer/sourcemap/sourcemap.ts @@ -25,7 +25,7 @@ export class SourceMap { * Sources map * @private */ - private sourcesMap: string[] = []; + private sourcesMap: number[] = []; /** * Sources content @@ -102,14 +102,31 @@ export class SourceMap { } } + hasSourceContent(id: number): boolean { + return this.sourcesMap.includes(id); + } + + addSourceContent(id: number, fileName: string | null, content: string | null): void { + + if (this.sourcesMap.includes(id)) { + return; + } + + this.sourcesMap[this.sourcesMap.length] = id; + this.sources[this.sources.length] = fileName; + this.sourcesContent[this.sourcesContent.length] = content; + } + /** * Add all location * @param maps */ - addAll(maps: Array<[number, number, number, number, number, string | null, string | null]>): void { - for (let [newLine, newColumn, srcId, ln, col, sourceFileName, sourceContent] of maps) { - const key = `${srcId}:${ln}:${sourceFileName}:${col}:${newLine}:${newColumn}:${sourceContent}`; - const sourcemap = `${srcId}:${sourceFileName}:${sourceContent}`; + addAll(maps: Array<[number, number, number, number, number]>): void { + + let srcIndex: number; + for (let [newLine, newColumn, srcId, ln, col] of maps) { + const key = `${srcId}:${ln}:${col}:${newLine}:${newColumn}`; + const sourcemap = `${srcId}`; if (this.keys.has(key)) { continue; @@ -117,12 +134,6 @@ export class SourceMap { this.keys.add(key); - if (!this.sourcesMap.includes(sourcemap)) { - this.sourcesMap.push(sourcemap); - this.sources.push((sourceFileName as string) || null); - this.sourcesContent.push((sourceFileName != null ? null : sourceContent) || null); - } - const line: number = newLine - 1; let record: number[]; @@ -130,8 +141,15 @@ export class SourceMap { this.line = line; } + srcIndex = this.sourcesMap.indexOf(srcId); + + if (srcIndex == -1) { + + throw new Error(`Source file ${srcId} not added to sourcemap`); + } + if (!this.map.has(line)) { - record = [Math.max(0, newColumn - 1), this.sourcesMap.indexOf(sourcemap), ln - 1, col - 1]; + record = [Math.max(0, newColumn - 1), srcIndex, ln - 1, col - 1]; this.map.set(line, [record]); } else { @@ -139,7 +157,7 @@ export class SourceMap { record = [ Math.max(0, newColumn - 1) - arr[0][0], - this.sourcesMap.indexOf(sourcemap) - arr[0][1], + srcIndex - arr[0][1], ln - 1, col - 1, ]; diff --git a/src/lib/syntax/syntax.ts b/src/lib/syntax/syntax.ts index 799cfa68..45098b17 100644 --- a/src/lib/syntax/syntax.ts +++ b/src/lib/syntax/syntax.ts @@ -1081,7 +1081,6 @@ export function isColor(token: Token, errors?: ErrorDescription[]): boolean { action: "drop", message: `Invalid color`, node: token, - // location: options.source!.getSourLocation(token[LOC]!.sta), }); return false; } @@ -1107,76 +1106,10 @@ export function isColor(token: Token, errors?: ErrorDescription[]): boolean { return true; } - // if (children.length < 4 || children.length > 8) { - // return false; - // } - - // if (!isRelative && !isColorspace(children[0])) { - // return false; - // } - - for (let i = 1; i < children.length - 2; i++) { - // if (children[i].typ == EnumToken.IdenTokenType) { - // if (isColor(children[i])) { - // continue; - // } - // if ( - // (children[i] as IdentToken).val != "none" && - // !( - // (isRelative && - // (["alpha", "r", "g", "b", "x", "y", "z"] as string[]).includes( - // (children[i] as IdentToken).val, - // )) || - // isColorspace(children[i]) - // ) - // ) { - // return false; - // } - // } - // if (children[i].typ === EnumToken.WildCardFunctionTokenType) { - // continue; - // } - // if ( - // children[i].typ === EnumToken.FunctionTokenType || - // children[i].typ === EnumToken.MathFunctionTokenType - // ) { - // if (!mathFuncs.includes((children[i] as FunctionToken).val)) { - // return false; - // } - // } - } - if (children.length == 4 || (isRelative && children.length == 6)) { return true; } - if (children.length == 8 || children.length == 6) { - const sep: Token = children.at(-2) as Token; - const alpha: Token = children.at(-1) as Token; - // @ts-ignore - // if ( - // ((children.length > 6 || !isRelative) && sep.typ != EnumToken.LiteralTokenType) || - // (sep as LiteralToken).val != "/" - // ) { - // return false; - // } - - // if (alpha.typ == EnumToken.IdenTokenType && (alpha as IdentToken).val != "none") { - // return false; - // } else { - // // @ts-ignore - // if (alpha.typ == EnumToken.PercentageTokenType) { - // if (+(alpha as PercentageToken).val < 0 || +(alpha as PercentageToken).val > 100) { - // return false; - // } - // } else if (alpha.typ == EnumToken.NumberTokenType) { - // if (+(alpha as NumberToken).val < 0 || +(alpha as NumberToken).val > 1) { - // return false; - // } - // } - // } - } - return true; } // @ts-ignore @@ -1197,10 +1130,6 @@ export function isColor(token: Token, errors?: ErrorDescription[]): boolean { [[]] as Token[][], ); - // if (children.length === 0 || children[0].length === 0) { - // return false; - // } - let j: number = 0; let k: number = 0; @@ -1239,58 +1168,14 @@ export function isColor(token: Token, errors?: ErrorDescription[]): boolean { k++; } } - // else { - // return false; - // } } else { k++; } } - // else { - // return false; - // } - - // if (k != children[j].length) { - // return false; - // } j++; } - // while (j < children.length) { - // if (children[j].length > 2) { - // return false; - // } - - // if ( - // !isColor(children[j][0]) && - // !( - // children[j][0].typ == EnumToken.WildCardFunctionTokenType && - // equalsIgnoreCase("calc", (children[j][0] as FunctionToken).val) - // ) - // ) { - // return false; - // } - - // if (children[j][0].typ == EnumToken.WildCardFunctionTokenType) { - // const result = matchAllSyntaxes( - // getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, "calc()") as ValidationFunctionToken[], - // createValidationContext([children[j][0]]), - // {}, - // ); - - // if (!result.success) { - // return false; - // } - // } - - // if (children[j].length > 1 && !isPercentageToken(children[j][1])) { - // return false; - // } - - // j++; - // } - return true; } else { const keywords: string[] = ["from", "none"]; @@ -1312,25 +1197,6 @@ export function isColor(token: Token, errors?: ErrorDescription[]): boolean { } if (v.typ == EnumToken.IdenTokenType) { - // if (isColor(v)) { - // continue; - // } - - // if (!(keywords.includes(v.val) || COLORS_NAMES[v.val.toLowerCase()] != null)) { - // return false; - // } - - // if (keywords.includes(v.val)) { - // if (isLegacySyntax) { - // return false; - // } - - // // @ts-ignore - // if (v.val == "from" && ["rgba", "hsla"].includes((token as ColorToken).val)) { - // return false; - // } - // } - continue; } @@ -1341,21 +1207,6 @@ export function isColor(token: Token, errors?: ErrorDescription[]): boolean { ) { continue; } - - // if ( - // ![ - // EnumToken.ColorTokenType, - // EnumToken.IdenTokenType, - // EnumToken.NumberTokenType, - // EnumToken.AngleTokenType, - // EnumToken.PercentageTokenType, - // EnumToken.CommaTokenType, - // EnumToken.WhitespaceTokenType, - // EnumToken.LiteralTokenType, - // ].includes(v.typ) - // ) { - // return false; - // } } } @@ -1424,75 +1275,13 @@ export function parseColor(token: Token) { if ((token as ColorToken).val == "color") { let index: number = (token as ColorToken).chi!.indexOf(tk) as number; - // if ((token as ColorToken).cal == "rel") { - // for (let k = 0; k < (token as ColorToken).chi!.length; k++) { - // if (EnumToken.DashedIdenTokenType == (token as ColorToken).chi![k].typ) { - // index = k; - // break; - // } - // } - // } - if (EnumToken.DashedIdenTokenType == (token as ColorToken)?.chi?.[index]?.typ) { (token as ColorToken).kin = ColorType.CUSTOM_COLOR; } } } - - // return token; } - // @ts-ignore - // token.typ = EnumToken.ColorTokenType; - - // // @ts-ignore - // (token as ColorToken).kin = ColorType[token.val.replaceAll("-", "_").toUpperCase()]; - - // if (!("chi" in token)) { - // const val: string = (token as ColorToken).val.toLowerCase(); - - // if (val == "currentcolor" || val == "transparent" || val in COLORS_NAMES) { - // (token as ColorToken).kin = ColorType.LIT; - // } else if (isHexColor(val)) { - // (token as ColorToken).kin = ColorType.HEX; - // } - - // const tk = (token as ColorToken).chi?.find( - // (t) => t.typ !== EnumToken.WhitespaceTokenType && t.typ !== EnumToken.CommentTokenType, - // ); - - // if (tk?.typ === EnumToken.IdenTokenType && (tk as IdentToken).val === "from") { - // (token as ColorToken).cal = "rel"; - // } else if ((token as ColorToken).val == "color-mix" && (tk as IdentToken).val == "in") { - // (token as ColorToken).cal = "mix"; - // } else if ((token as ColorToken).val == "color") { - // (token as ColorToken).cal = "col"; - // } - - // return token; - // } - - // // @ts-ignore - // if (((token as ColorToken).chi as Token[])[0].typ == EnumToken.IdenTokenType) { - // // @ts-ignore - // if (((token as ColorToken).chi as Token[])[0].val == "from") { - // // @ts-ignore - // (token as ColorToken).cal = "rel"; - // } - - // // @ts-ignore - // else if ((token as ColorToken).val == "color-mix" && ((token as ColorToken).chi as Token[])[0].val == "in") { - // // @ts-ignore - // (token as ColorToken).cal = "mix"; - // } else { - // // @ts-ignore - // if ((token as ColorToken).val == "color") { - // // @ts-ignore - // (token as ColorToken).cal = "col"; - // } - // } - // } - return token; } @@ -1601,6 +1390,45 @@ export function isNonPrintable(codepoint: number): boolean { ); } +export function isURLToken(str: string): boolean { + let i: number = -1; + let c: number; + + while (++i < str.length) { + c = str.charCodeAt(i) as number; + + // single quote or double quote or start parenthesis or close parenthesis + if (isNonPrintable(c) || c == 0x27 || c == 0x22 || c == 0x28 || c == 0x29) { + return false; + } + + // valid escape + if (c == REVERSE_SOLIDUS) { + i++; + + if (i >= str.length) { + return false; + } + + c = str.charCodeAt(i) as number; + + // c is not '\n' or '\r' or '\f' + if (c == 0x6e || c == 0x72 || c == 0x66) { + return false; + } + + continue; + } + + // is white space + if (c == 0x20 || c == 0x09) { + break; + } + } + + return i == str.length; +} + export function isPseudo(name: string): boolean { return ( name.charAt(0) == ":" && @@ -1610,14 +1438,10 @@ export function isPseudo(name: string): boolean { } export function isHash(name: string): boolean { - return name.charAt(0) == "#" && isIdent(name.charAt(1)); + return name.charAt(0) == "#" && isIdentStart(name.charCodeAt(1)); } export const isNumber = memoize(function (name: string): boolean { - // if (name.length == 0) { - // return false; - // } - let codepoint: number = name.charCodeAt(0) as number; let i: number = 0; const j: number = name.length; diff --git a/test/allFiles.js b/test/allFiles.js index e82da112..819b7efc 100644 --- a/test/allFiles.js +++ b/test/allFiles.js @@ -7,7 +7,7 @@ const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'], e = Math.floor(Math.log(this) function toFileSize(value) { const e = Math.floor(Math.log(value) / Math.log(1024)); - const result = value === 0 ? 0 : (value / Math.pow(1024, Math.floor(e))); + const result = (value / Math.pow(1024, Math.floor(e))); return (Number.isInteger(result) ? result : result.toFixed(2)) + units[e]; } @@ -24,14 +24,14 @@ for (const file of await readdir(baseDir)) { removePrefix: true, nestingRules: true, resolveImport: true, - // sourcemap: true, + sourcemap: true, validation: true })); message += `[inputSize]: ${toFileSize(result.stats.bytesIn)}\n `; message += `[outputSize]: ${toFileSize(result.stats.bytesOut)}\n `; message += `[ratio]: ${(100 * (1 - result.stats.bytesOut / result.stats.bytesIn)).toFixed(2)}%\n `; - // message += `[sourcemap]: ${JSON.stringify(result.map.toJSON()).length}\n `; + message += `[sourcemap]: ${JSON.stringify(result.map.toJSON()).length}\n `; for (const key in result.stats) { diff --git a/test/specs/code/modules.js b/test/specs/code/modules.js index d038667e..9498898b 100644 --- a/test/specs/code/modules.js +++ b/test/specs/code/modules.js @@ -1,6 +1,22 @@ import { ColorType, EnumToken, ModuleCaseTransformEnum, ModuleScopeEnumOptions } from "../../../dist/lib/ast/types.js"; -export function run(describe, expect, it, transform, parse, render, dirname, readFile, resolve, ColorType, EnumToken, ModuleCaseTransformEnum, ModuleScopeEnumOptions, transformSync, parseSync) { +export function run( + describe, + expect, + it, + transform, + parse, + render, + dirname, + readFile, + resolve, + ColorType, + EnumToken, + ModuleCaseTransformEnum, + ModuleScopeEnumOptions, + transformSync, + parseSync, +) { describe("css modules", function () { it("module #1", function () { return transform( @@ -99,8 +115,7 @@ export function run(describe, expect, it, transform, parse, render, dirname, rea }); it("module #4", function () { - - const url = new URL(dirname(import.meta.url) + '/../../css-modules/mixins.css'); + const url = new URL(dirname(import.meta.url) + "/../../css-modules/mixins.css"); return transform( ` @@ -613,9 +628,7 @@ a span { }); it("module mode ICSS #17", function () { - - - const url = new URL(dirname(import.meta.url) + '/../../css-modules/mixins.css'); + const url = new URL(dirname(import.meta.url) + "/../../css-modules/mixins.css"); return transform( ` @@ -673,60 +686,59 @@ a span { }); }); -// it("module export variables #18", function () { -// return transform( -// ` - -// @value blue: #0c77f8; -// @value red: #ff0000; -// @value green: #aaf200; -// `, -// { -// module: ModuleScopeEnumOptions.ICSS, -// beautify: true, -// }, -// ).then((result) => -// expect(result.cssModuleVariables).deep.equals({ -// blue: { -// typ: EnumToken.CssVariableTokenType, -// nam: "blue", -// val: [ -// { -// typ: EnumToken.ColorTokenType, -// val: "#0c77f8", -// kin: ColorType.HEX, -// }, -// ], -// }, -// red: { -// typ: EnumToken.CssVariableTokenType, -// nam: "red", -// val: [ -// { -// typ: EnumToken.ColorTokenType, -// val: "#ff0000", -// kin: ColorType.HEX, -// }, -// ], -// }, -// green: { -// typ: EnumToken.CssVariableTokenType, -// nam: "green", -// val: [ -// { -// typ: EnumToken.ColorTokenType, -// val: "#aaf200", -// kin: ColorType.HEX, -// }, -// ], -// }, -// }), -// ); -// }); + // it("module export variables #18", function () { + // return transform( + // ` + + // @value blue: #0c77f8; + // @value red: #ff0000; + // @value green: #aaf200; + // `, + // { + // module: ModuleScopeEnumOptions.ICSS, + // beautify: true, + // }, + // ).then((result) => + // expect(result.cssModuleVariables).deep.equals({ + // blue: { + // typ: EnumToken.CssVariableTokenType, + // nam: "blue", + // val: [ + // { + // typ: EnumToken.ColorTokenType, + // val: "#0c77f8", + // kin: ColorType.HEX, + // }, + // ], + // }, + // red: { + // typ: EnumToken.CssVariableTokenType, + // nam: "red", + // val: [ + // { + // typ: EnumToken.ColorTokenType, + // val: "#ff0000", + // kin: ColorType.HEX, + // }, + // ], + // }, + // green: { + // typ: EnumToken.CssVariableTokenType, + // nam: "green", + // val: [ + // { + // typ: EnumToken.ColorTokenType, + // val: "#aaf200", + // kin: ColorType.HEX, + // }, + // ], + // }, + // }), + // ); + // }); it("module import variables #19", function () { - - const url = new URL(dirname(import.meta.url) + '/../../css-modules/color.css'); + const url = new URL(dirname(import.meta.url) + "/../../css-modules/color.css"); return transform( ` @@ -866,9 +878,9 @@ a span { }); it("module grid #22", function () { - - return expect(transform( - ` + return expect( + transform( + ` .grid { grid-template-areas: 'nav main'; @@ -881,15 +893,16 @@ a span { } `, - { - module: { - pattern: "[local]-[hash:bogus]", + { + module: { + pattern: "[local]-[hash:bogus]", + }, + beautify: true, }, - beautify: true, - }, - )).to.be.rejectedWith( - `Unsupported hash length: 'bogus'. expecting format [hash:length] or [hash:hash-algo:length]`, - ); + ), + ).to.be.rejectedWith( + `Unsupported hash length: 'bogus'. expecting format [hash:length] or [hash:hash-algo:length]`, + ); }); it("module grid #23", function () { @@ -931,7 +944,7 @@ a span { }); }); - it("module #24", function () { + it("module #24", function () { const result = transformSync( ` .goal .bg-indigo { @@ -948,19 +961,50 @@ a span { beautify: true, }, ); - - expect(result.mapping).deep.equals({ - goal: "goal_r7bhp", - "bg-indigo": "bg-indigo_gy28g", - "indigo-white": "indigo-white_wims0 bg-indigo_gy28g title_qw06e", - title: "title_qw06e", - }); - expect(result.code).equals(`.goal_r7bhp .bg-indigo_gy28g { + + expect(result.mapping).deep.equals({ + goal: "goal_r7bhp", + "bg-indigo": "bg-indigo_gy28g", + "indigo-white": "indigo-white_wims0 bg-indigo_gy28g title_qw06e", + title: "title_qw06e", + }); + expect(result.code).equals(`.goal_r7bhp .bg-indigo_gy28g { background: indigo } .indigo-white_wims0 { color: #fff }`); }); + + it("module grid #25", function () { + return expect( + Promise.resolve( + (async () => + transformSync( + ` + +.grid { + grid-template-areas: 'nav main'; + grid-template-columns: [line-name1] 100px [line-name2 line-name3]; +} + +.nav { + grid-column-start: nav-start; + grid-column-end: nav-end; +} + +`, + { + module: { + pattern: "[local]-[hash:sha1]", + }, + beautify: true, + }, + ))(), + ), + ).to.be.rejectedWith( + `Unsupported hash algorithm: 'sha1'. Not supported by parseSync() or transformSync(). Use parse() or transform().`, + ); + }); }); } diff --git a/test/specs/code/prefix.js b/test/specs/code/prefix.js index 0d8e9056..ac9431d8 100644 --- a/test/specs/code/prefix.js +++ b/test/specs/code/prefix.js @@ -1,10 +1,24 @@ - -export function run(describe, expect, it, transform, parse, render, dirname, readFile) { - - describe('prefix removal', function () { - - it('selector prefix #1', function () { - return transform(` +export function run( + describe, + expect, + it, + transform, + parse, + render, + dirname, + readFile, + resolve, + ColorType, + EnumToken, + ModuleCaseTransformEnum, + ModuleScopeEnumOptions, + transformSync, + parseSync, +) { + describe("prefix removal", function () { + it("selector prefix #1", function () { + return transform( + ` @media screen { @@ -12,15 +26,20 @@ export function run(describe, expect, it, transform, parse, render, dirname, rea height: calc(100px * 2/ 15); } } -`, {removePrefix: true}).then(result => expect(render(result.ast, {minify: false}).code).equals(`@media screen { +`, + { removePrefix: true }, + ).then((result) => + expect(render(result.ast, { minify: false }).code).equals(`@media screen { .foo:autofill:not(:hover) { height: calc(40px/3) } -}`)); +}`), + ); }); - it('selector prefix #2', function () { - return transform(` + it("selector prefix #2", function () { + return transform( + ` @media screen { @@ -29,15 +48,20 @@ export function run(describe, expect, it, transform, parse, render, dirname, rea display: none; } } -`, {removePrefix: true, nestingRules: false}).then(result => expect(render(result.ast, {minify: false}).code).equals(`@media screen { +`, + { removePrefix: true, nestingRules: false }, + ).then((result) => + expect(render(result.ast, { minify: false }).code).equals(`@media screen { .foo:is(:autofill:not(:hover),a,b) { display: none } -}`)); +}`), + ); }); - it('selector unknown prefix #3', function () { - return transform(` + it("selector unknown prefix #3", function () { + return transform( + ` @media screen { @@ -45,15 +69,20 @@ export function run(describe, expect, it, transform, parse, render, dirname, rea height: calc(100px * 2/ 15); } } -`, {removePrefix: true}).then(result => expect(render(result.ast, {minify: false}).code).equals(`@media screen { +`, + { removePrefix: true }, + ).then((result) => + expect(render(result.ast, { minify: false }).code).equals(`@media screen { .foo:autofill:not(:hover) { height: calc(40px/3) } -}`)); +}`), + ); }); - it('selector invalid prefix #4', function () { - return transform(` + it("selector invalid prefix #4", function () { + return transform( + ` @media screen { @@ -61,11 +90,14 @@ export function run(describe, expect, it, transform, parse, render, dirname, rea height: calc(100px * 2/ 15); } } -`, {removePrefix: true}).then(result => expect(result.code).equals(``)); +`, + { removePrefix: true }, + ).then((result) => expect(result.code).equals(``)); }); - it('prefixed properties #4', function () { - return transform(` + it("prefixed properties #4", function () { + return transform( + ` a:any-link { border: 1px solid blue; @@ -89,7 +121,10 @@ a:-webkit-any-link { -webkit-appearance: none;; } } -`, {removePrefix: true, beautify: true}).then(result => expect(result.code).equals(`a:any-link { +`, + { removePrefix: true, beautify: true }, + ).then((result) => + expect(result.code).equals(`a:any-link { border: 1px solid blue; color: orange } @@ -103,11 +138,13 @@ a:-webkit-any-link { appearance: none } } -}`)); +}`), + ); }); - it('all prefixed #5', function () { - return transform(` + it("all prefixed #5", function () { + return transform( + ` ::-webkit-input-placeholder { color: gray; @@ -226,11 +263,14 @@ a { .xl\\:origin-bottom-left2 { background: -o-linear-gradient(-90deg,#fff,#000); } -`, { - removePrefix: true, - removeDuplicateDeclarations: false, - beautify: true - }).then(result => expect(result.code).equals(`::placeholder { +`, + { + removePrefix: true, + removeDuplicateDeclarations: false, + beautify: true, + }, + ).then((result) => + expect(result.code).equals(`::placeholder { color: grey } @supports selector(::placeholder) { @@ -304,11 +344,13 @@ a { } .xl\\:origin-bottom-left2 { background: linear-gradient(#fff,#000) -}`)); +}`), + ); }); - it('do not mix #6', function () { - return transform(` + it("do not mix #6", function () { + return transform( + ` ::moz-selection { opacity: .25; @@ -325,9 +367,12 @@ a { filter: blur(25px); } } -`, { - beautify: true - }).then(result => expect(result.code).equals(`::moz-selection { +`, + { + beautify: true, + }, + ).then((result) => + expect(result.code).equals(`::moz-selection { opacity: .25; filter: blur(25px) } @@ -338,8 +383,75 @@ a { ::selection { opacity: .25; filter: blur(25px) -}`)); +}`), + ); }); + it("do not mix #7", function () { + const result = transformSync( + ` + +@media (-webkit-max-device-pixel-ratio: 2) { +#converted-text { + color: #00b400; + background: -webkit-gradient(linear,left bottom, left top,from(#fff),to(#000)); +} + +.hsl { + color: #00b400; + background: -webkit-gradient(linear,left top, right top,from(#fff),to(#000)); +} +.bg { + color: #00b400; + background: -webkit-gradient(linear,right top, left top,from(#fff),to(#000)); +} +.bg2 { + color: #00b400; + background: -webkit-gradient(linear,right top, left bottom,from(#fff),to(#000)); +} +.bg3 { + color: #00b400; + background: -webkit-gradient(linear,left bottom, right top,from(#fff),to(#000)); +} +.bg4 { + color: #00b400; + background: -webkit-gradient(linear,right bottom, left top,from(#fff),to(#000)); +} +`, + { + beautify: true, + removePrefix: true, + }, + ); + + expect(result.code).equals(`@media (max-resolution:2x) { + #converted-text { + color: #00b400; + background: linear-gradient(0,#fff,#000) + } + .hsl,.bg { + color: #00b400 + } + .bg { + background: linear-gradient(270deg,#fff,#000) + } + .hsl { + background: linear-gradient(90deg,#fff,#000) + } + .bg2,.bg3 { + color: #00b400 + } + .bg3 { + background: linear-gradient(to top right,#fff,#000) + } + .bg2 { + background: linear-gradient(to bottom left,#fff,#000) + } + .bg4 { + color: #00b400; + background: linear-gradient(to top left,#fff,#000) + } +}`); + }); }); -} \ No newline at end of file +} diff --git a/test/specs/code/visitors.js b/test/specs/code/visitors.js index 66a99817..90d0c90f 100644 --- a/test/specs/code/visitors.js +++ b/test/specs/code/visitors.js @@ -425,6 +425,11 @@ html,body { it("visitor #8", function () { const css = ` + .ruler { + + height: 10px; + } + @keyframes slide-in { from { transform: translateX(0%); @@ -456,6 +461,24 @@ html,body { removePrefix: true, beautify: true, visitor: { + Rule: { + type: WalkerEvent.Enter, + handler(node) { + + node.sel = ".ruled"; + // node.val = "slide-in-out"; + // return node; + }, + }, + Declaration: { + type: WalkerEvent.Leave, + handler(node) { + + // node.sel = ".ruled"; + // node.val = "slide-in-out"; + // return node; + }, + }, KeyframesAtRule: { slideIn(node) { node.val = "slide-in-out"; @@ -467,7 +490,10 @@ html,body { const result = transformSync(css, options); - expect(result.code).equals(`@keyframes slide-in-out { + expect(result.code).equals(`.ruled { + height: 10px +} +@keyframes slide-in-out { 0% { transform: translateX(0) } @@ -517,12 +543,17 @@ body { declaration.nam = "width"; } }, - function ColorTokenType(color) { - return { - typ: EnumToken.Color, - val: "red", - kin: ColorType.HEX, - }; + { + ColorTokenType: { + type: WalkerEvent.Enter, + handler: function (color) { + return { + typ: EnumToken.Color, + val: "red", + kin: ColorType.HEX, + }; + }, + }, }, ], }; @@ -540,5 +571,123 @@ html,body { }`), ); }); + + + + it("visitor #10", function () { + const css = ` + + .ruler { + + height: 10px; + } + +@keyframes slide-in { + from { + transform: translateX(0%); + } + + to { + transform: translateX(100%); + } +} +@keyframes identifier { + 0% { + top: 0; + left: 0; + } + 30% { + top: 50px; + } + 68%, + 72% { + left: 50px; + } + 100% { + top: 100px; + left: 100%; + } +} +`; + const options = { + removePrefix: true, + beautify: true, + visitor: [ + { + Rule: { + type: WalkerEvent.Enter, + handler(node) { + node.sel = ".ruled"; + // node.val = "slide-in-out"; + // return node; + }, + }, + }, + { + KeyframesAtRule: { + slideIn(node) { + node.val = "slide-in-out"; + return node; + }, + }, + }, + ], + }; + + const result = transformSync(css, options); + + expect(result.code).equals(`.ruled { + height: 10px +} +@keyframes slide-in-out { + 0% { + transform: translateX(0) + } + to { + transform: translateX(100%) + } +} +@keyframes identifier { + 0% { + top: 0; + left: 0 + } + 30% { + top: 50px + } + 68%,72% { + left: 50px + } + to { + top: 100px; + left: 100% + } +}`); + }); + + it("visitor #11", function () { + const css = ` + + .ruler { + + height: 10px; + } +`; + const options = { + removePrefix: true, + beautify: true, + visitor: function Rule(node) { + node.sel = ".ruled"; + // node.val = "slide-in-out"; + // return node; + }, +}; + + const result = transformSync(css, options); + + expect(result.code).equals(`.ruled { + height: 10px +}`); + }); }); } From 52223b9835bd8d898dd6ace62575f75f778ef40e Mon Sep 17 00:00:00 2001 From: Thierry Bela Nanga Date: Tue, 18 Aug 2026 20:01:17 -0400 Subject: [PATCH 13/22] fix sourcemap performance regression #146 --- dist/index-umd-web.js | 224 ++++------------------- dist/index.cjs | 224 ++++------------------- dist/index.d.ts | 1 + dist/lib/ast/features/if.js | 8 +- dist/lib/parser/linesmap.js | 3 +- dist/lib/parser/parse.js | 12 +- dist/lib/parser/tokenize.js | 32 ++-- dist/lib/renderer/render.js | 6 +- dist/lib/renderer/sourcemap/sourcemap.js | 12 +- dist/lib/syntax/syntax.js | 149 --------------- dist/node.js | 4 +- dist/web.js | 4 +- src/lib/ast/features/if.ts | 10 +- src/lib/parser/linesmap.ts | 3 +- src/lib/parser/parse.ts | 12 +- src/lib/parser/tokenize.ts | 63 +++---- src/lib/renderer/render.ts | 6 +- src/lib/renderer/sourcemap/sourcemap.ts | 16 +- src/node.ts | 4 +- src/web.ts | 4 +- test/specs/code/sourcemaps.js | 10 +- 21 files changed, 184 insertions(+), 623 deletions(-) diff --git a/dist/index-umd-web.js b/dist/index-umd-web.js index 993990e9..47223380 100644 --- a/dist/index-umd-web.js +++ b/dist/index-umd-web.js @@ -15606,7 +15606,6 @@ action: "drop", message: `Invalid color`, node: token, - // location: options.source!.getSourLocation(token[LOC]!.sta), }); return false; } @@ -15629,31 +15628,6 @@ if (children.length == 4 || (isRelative && children.length == 6)) { return true; } - if (children.length == 8 || children.length == 6) { - children.at(-2); - children.at(-1); - // @ts-ignore - // if ( - // ((children.length > 6 || !isRelative) && sep.typ != EnumToken.LiteralTokenType) || - // (sep as LiteralToken).val != "/" - // ) { - // return false; - // } - // if (alpha.typ == EnumToken.IdenTokenType && (alpha as IdentToken).val != "none") { - // return false; - // } else { - // // @ts-ignore - // if (alpha.typ == EnumToken.PercentageTokenType) { - // if (+(alpha as PercentageToken).val < 0 || +(alpha as PercentageToken).val > 100) { - // return false; - // } - // } else if (alpha.typ == EnumToken.NumberTokenType) { - // if (+(alpha as NumberToken).val < 0 || +(alpha as NumberToken).val > 1) { - // return false; - // } - // } - // } - } return true; } // @ts-ignore @@ -15670,9 +15644,6 @@ } return acc; }, [[]]); - // if (children.length === 0 || children[0].length === 0) { - // return false; - // } let j = 0; let k = 0; if (children[j][0].typ === exports.EnumToken.IdenTokenType && @@ -15706,50 +15677,13 @@ k++; } } - // else { - // return false; - // } } else { k++; } } - // else { - // return false; - // } - // if (k != children[j].length) { - // return false; - // } j++; } - // while (j < children.length) { - // if (children[j].length > 2) { - // return false; - // } - // if ( - // !isColor(children[j][0]) && - // !( - // children[j][0].typ == EnumToken.WildCardFunctionTokenType && - // equalsIgnoreCase("calc", (children[j][0] as FunctionToken).val) - // ) - // ) { - // return false; - // } - // if (children[j][0].typ == EnumToken.WildCardFunctionTokenType) { - // const result = matchAllSyntaxes( - // getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, "calc()") as ValidationFunctionToken[], - // createValidationContext([children[j][0]]), - // {}, - // ); - // if (!result.success) { - // return false; - // } - // } - // if (children[j].length > 1 && !isPercentageToken(children[j][1])) { - // return false; - // } - // j++; - // } return true; } else { @@ -15762,21 +15696,6 @@ // @ts-ignore for (const v of token.chi) { if (v.typ == exports.EnumToken.IdenTokenType) { - // if (isColor(v)) { - // continue; - // } - // if (!(keywords.includes(v.val) || COLORS_NAMES[v.val.toLowerCase()] != null)) { - // return false; - // } - // if (keywords.includes(v.val)) { - // if (isLegacySyntax) { - // return false; - // } - // // @ts-ignore - // if (v.val == "from" && ["rgba", "hsla"].includes((token as ColorToken).val)) { - // return false; - // } - // } continue; } if (v.typ === exports.EnumToken.MathFunctionTokenType || @@ -15784,20 +15703,6 @@ colorsFunc.includes(v.val)) { continue; } - // if ( - // ![ - // EnumToken.ColorTokenType, - // EnumToken.IdenTokenType, - // EnumToken.NumberTokenType, - // EnumToken.AngleTokenType, - // EnumToken.PercentageTokenType, - // EnumToken.CommaTokenType, - // EnumToken.WhitespaceTokenType, - // EnumToken.LiteralTokenType, - // ].includes(v.typ) - // ) { - // return false; - // } } } return true; @@ -15853,63 +15758,12 @@ } if (token.val == "color") { let index = token.chi.indexOf(tk); - // if ((token as ColorToken).cal == "rel") { - // for (let k = 0; k < (token as ColorToken).chi!.length; k++) { - // if (EnumToken.DashedIdenTokenType == (token as ColorToken).chi![k].typ) { - // index = k; - // break; - // } - // } - // } if (exports.EnumToken.DashedIdenTokenType == token?.chi?.[index]?.typ) { token.kin = exports.ColorType.CUSTOM_COLOR; } } } - // return token; } - // @ts-ignore - // token.typ = EnumToken.ColorTokenType; - // // @ts-ignore - // (token as ColorToken).kin = ColorType[token.val.replaceAll("-", "_").toUpperCase()]; - // if (!("chi" in token)) { - // const val: string = (token as ColorToken).val.toLowerCase(); - // if (val == "currentcolor" || val == "transparent" || val in COLORS_NAMES) { - // (token as ColorToken).kin = ColorType.LIT; - // } else if (isHexColor(val)) { - // (token as ColorToken).kin = ColorType.HEX; - // } - // const tk = (token as ColorToken).chi?.find( - // (t) => t.typ !== EnumToken.WhitespaceTokenType && t.typ !== EnumToken.CommentTokenType, - // ); - // if (tk?.typ === EnumToken.IdenTokenType && (tk as IdentToken).val === "from") { - // (token as ColorToken).cal = "rel"; - // } else if ((token as ColorToken).val == "color-mix" && (tk as IdentToken).val == "in") { - // (token as ColorToken).cal = "mix"; - // } else if ((token as ColorToken).val == "color") { - // (token as ColorToken).cal = "col"; - // } - // return token; - // } - // // @ts-ignore - // if (((token as ColorToken).chi as Token[])[0].typ == EnumToken.IdenTokenType) { - // // @ts-ignore - // if (((token as ColorToken).chi as Token[])[0].val == "from") { - // // @ts-ignore - // (token as ColorToken).cal = "rel"; - // } - // // @ts-ignore - // else if ((token as ColorToken).val == "color-mix" && ((token as ColorToken).chi as Token[])[0].val == "in") { - // // @ts-ignore - // (token as ColorToken).cal = "mix"; - // } else { - // // @ts-ignore - // if ((token as ColorToken).val == "color") { - // // @ts-ignore - // (token as ColorToken).cal = "col"; - // } - // } - // } return token; } function isLetter(codepoint) { @@ -16029,9 +15883,6 @@ return name.charAt(0) == "#" && isIdentStart(name.charCodeAt(1)); } const isNumber = memoize(function (name) { - // if (name.length == 0) { - // return false; - // } let codepoint = name.charCodeAt(0); let i = 0; const j = name.length; @@ -21409,6 +21260,9 @@ nam: left.val, chi: [], }); + if (declaration[PARENT] != null) { + atRule[LOC] = declaration[PARENT][LOC]; + } atRule[TOKENS] = [{ typ: exports.EnumToken.ParensTokenType, chi: left.chi.slice() }]; const minify = atRule.nam !== "supports"; const options = { @@ -21431,6 +21285,9 @@ }); atRule[TOKENS] = [left]; atRule.val = atRule[TOKENS].reduce((acc, curr) => acc + renderValue(curr), ""); + if (declaration[PARENT] != null) { + atRule[LOC] = declaration[PARENT][LOC]; + } clonedDeclaration = cloneNode(declaration, true, nodeMap); replaceNodeOrValue(nodeMap.get(targetWrapper.typ === exports.EnumToken.WildCardFunctionTokenType ? targetParentWrapper : targetWrapper), nodeMap.get(targetWrapper.typ === exports.EnumToken.WildCardFunctionTokenType ? targetWrapper : node), node.r.at(-1)?.typ === exports.EnumToken.SemiColonTokenType ? trimArray(node.r.slice(0, -1)) @@ -21664,12 +21521,13 @@ return; } this.sourcesMap[this.sourcesMap.length] = id; - this.sources[this.sources.length] = fileName; - this.sourcesContent[this.sourcesContent.length] = content; + this.sources[this.sources.length] = fileName || null; + this.sourcesContent[this.sourcesContent.length] = content || null; } /** * Add all location * @param maps + * @throws */ addAll(maps) { let srcIndex; @@ -21694,12 +21552,7 @@ } else { const arr = this.map.get(line); - record = [ - Math.max(0, newColumn - 1) - arr[0][0], - srcIndex - arr[0][1], - ln - 1, - col - 1, - ]; + record = [Math.max(0, newColumn - 1) - arr[0][0], srcIndex - arr[0][1], ln - 1, col - 1]; arr.push(record); } if (this.lastLocation != null) { @@ -21851,9 +21704,8 @@ if (offset < 0 || line < 0) { return [1, 1]; } - const column = offset - this.lineStarts[line] + 1; // [line, column] - return [line + 1, column == 0 ? 1 : column]; + return [line + 1, offset - this.lineStarts[line] + 1]; } /** * search the greatest index of the value less than or equal to offset @@ -22149,9 +22001,9 @@ let value; let buffer = quote; const result = []; - while ((value = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset + 1))) { + while ((value = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset))) { if (value == "\\") { - if ("\\" == parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset + 2)) { + if ("\\" == parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset + 1)) { buffer += next(parseInfo, 2); continue; } @@ -22187,7 +22039,7 @@ } next(parseInfo, escapeSequence.length + 1 + - (isWhiteSpace(parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset + 1)?.charCodeAt(0)) + (isWhiteSpace(parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset)?.charCodeAt(0)) ? 1 : 0)); continue; @@ -22344,12 +22196,12 @@ end: parseInfo.currentPosition, }; parseInfo.position = parseInfo.currentPosition; - return { token, bytesIn: parseInfo.currentPosition + 1 }; + return { token, bytesIn: parseInfo.currentPosition }; } function match(parseInfo, input) { let position = parseInfo.currentPosition - parseInfo.offset; for (let i = 0; i < input.length; i++) { - if (parseInfo.stream[position + i + 1] != input.charAt(i)) { + if (parseInfo.stream[position + i] != input.charAt(i)) { return false; } } @@ -22357,14 +22209,14 @@ } function peek(parseInfo, count = 1) { if (count == 1) { - return parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset + 1); + return parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset); } const position = parseInfo.currentPosition - parseInfo.offset; - return parseInfo.stream.slice(position + 1, position + count + 1); + return parseInfo.stream.slice(position, position + count); } function next(parseInfo, count = 1) { let position = parseInfo.currentPosition - parseInfo.offset; - let char = count == 1 ? parseInfo.stream.charAt(position + 1) : parseInfo.stream.slice(position + 1, position + 1 + count); + let char = count == 1 ? parseInfo.stream.charAt(position) : parseInfo.stream.slice(position, position + count); let i = 0; let codepoint; for (; i < char.length; i++) { @@ -22400,7 +22252,7 @@ offset: 0, time: 0, position: 0, - currentPosition: -1, + currentPosition: 0, }; } let value; @@ -22492,7 +22344,7 @@ value !== ")" && value !== ""); } - if (values) { + if (values != null) { if (peek(parseInfo) === "") { for (let i = 0; i < values.length; i++) { values[i].token.typ = exports.EnumToken.BadUrlTokenType; @@ -22572,15 +22424,13 @@ buffer = ""; } buffer += next(parseInfo); - nextCharCode = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset + 1).charCodeAt(0); + nextCharCode = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset).charCodeAt(0); while (nextCharCode == 0x20 || (nextCharCode >= 0x9 && nextCharCode <= 0xd) || nextCharCode == 0x2028 || nextCharCode == 0x2029) { value += next(parseInfo); - nextCharCode = parseInfo.stream - .charAt(parseInfo.currentPosition - parseInfo.offset + 1) - .charCodeAt(0); + nextCharCode = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset).charCodeAt(0); } result.push(yieldResult(value, parseInfo, exports.EnumToken.WhitespaceTokenType)); buffer = ""; @@ -22773,7 +22623,7 @@ break; case 46 /* TokenMap.DOT */: const codepoint = parseInfo.stream - .charAt(parseInfo.currentPosition - parseInfo.offset + 2) + .charAt(parseInfo.currentPosition - parseInfo.offset + 1) .charCodeAt(0); if (!isDigit(codepoint) && buffer !== "") { result.push(yieldResult(buffer, parseInfo)); @@ -22819,10 +22669,10 @@ parseInfo.stream = stream; } else { - parseInfo.stream = (parseInfo.stream.slice(parseInfo.currentPosition - parseInfo.offset + 1) + + parseInfo.stream = (parseInfo.stream.slice(parseInfo.currentPosition - parseInfo.offset) + stream); } - parseInfo.offset = parseInfo.currentPosition + 1; + parseInfo.offset = parseInfo.currentPosition; } yield* tokenize(parseInfo, done); if (done) { @@ -24995,6 +24845,7 @@ if ([exports.EnumToken.AtRuleNodeType, exports.EnumToken.KeyframesAtRuleNodeType].includes(data.typ) && !("chi" in data)) { return `${indent}@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val};`; } + const lineMapLength = linesMap ? linesMap.getLineStarts().length : 0; const prelude = (indent.length > 0 ? options.newLine : "") + indent + ([exports.EnumToken.AtRuleNodeType, exports.EnumToken.KeyframesAtRuleNodeType].includes(data.typ) @@ -25061,7 +24912,10 @@ sourceLocation.end--; } if (options.removeEmpty && children === "") { - sourceLocation.end -= prelude.length; + if (sourcemaps != null) { + sourceLocation.end -= prelude.length; + linesMap.getLineStarts().length = lineMapLength; + } return ""; } const end = options.newLine + indent + `}`; @@ -30026,7 +29880,7 @@ offset: 0, source, position: 0, - currentPosition: -1, + currentPosition: 0, }; const root = await doParse(stream instanceof ReadableStream ? tokenizeStream(stream, parseInfo) : tokenize(parseInfo), Object.assign({}, options, { minify: false, @@ -30336,7 +30190,7 @@ time: 0, source, position: 0, - currentPosition: -1, + currentPosition: 0, }; const root = await doParse(stream instanceof ReadableStream ? tokenizeStream(stream, parseInfo) : tokenize(parseInfo), Object.assign({}, options, { source, @@ -30482,7 +30336,7 @@ offset: 0, source: new SourceFile("", [], src.relative), position: 0, - currentPosition: -1, + currentPosition: 0, }) : tokenize({ stream, @@ -30490,7 +30344,7 @@ offset: 0, position: 0, source: new SourceFile(stream, [], src.relative), - currentPosition: -1, + currentPosition: 0, }), Object.assign({}, options, { minify: false, setParent: false, @@ -31811,7 +31665,7 @@ offset: 0, position: 0, source: new SourceFile(stream, [], ""), - currentPosition: -1, + currentPosition: 0, }), { setParent: false, minify: false, validation: false }).then((result) => { return result.ast.chi[0].chi.filter((t) => t.typ == exports.EnumToken.DeclarationNodeType || t.typ == exports.EnumToken.CommentNodeType); }); @@ -31846,7 +31700,7 @@ time: 0, source: new SourceFile(src, [], ""), position: 0, - currentPosition: -1, + currentPosition: 0, }; const tokenResults = tokenize(parseInfo); const mapped = []; @@ -32315,7 +32169,7 @@ time: 0, source: options.source, position: 0, - currentPosition: -1, + currentPosition: 0, }; const result = doParseSync(tokenize(options.parseInfo), options); return !options.module && !options.inputSourceMap && !options.sourcemap ? result : parseResult(result, options); @@ -32451,7 +32305,7 @@ time: 0, source: options.source, position: 0, - currentPosition: -1, + currentPosition: 0, }; return doParse(stream instanceof ReadableStream ? tokenizeStream(stream, options.parseInfo) : tokenize(options.parseInfo), options).then((result) => (!options.module && !options.inputSourceMap ? result : parseResult(result, options))); } diff --git a/dist/index.cjs b/dist/index.cjs index 597b006a..f24c74bd 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -15609,7 +15609,6 @@ function isColor(token, errors) { action: "drop", message: `Invalid color`, node: token, - // location: options.source!.getSourLocation(token[LOC]!.sta), }); return false; } @@ -15632,31 +15631,6 @@ function isColor(token, errors) { if (children.length == 4 || (isRelative && children.length == 6)) { return true; } - if (children.length == 8 || children.length == 6) { - children.at(-2); - children.at(-1); - // @ts-ignore - // if ( - // ((children.length > 6 || !isRelative) && sep.typ != EnumToken.LiteralTokenType) || - // (sep as LiteralToken).val != "/" - // ) { - // return false; - // } - // if (alpha.typ == EnumToken.IdenTokenType && (alpha as IdentToken).val != "none") { - // return false; - // } else { - // // @ts-ignore - // if (alpha.typ == EnumToken.PercentageTokenType) { - // if (+(alpha as PercentageToken).val < 0 || +(alpha as PercentageToken).val > 100) { - // return false; - // } - // } else if (alpha.typ == EnumToken.NumberTokenType) { - // if (+(alpha as NumberToken).val < 0 || +(alpha as NumberToken).val > 1) { - // return false; - // } - // } - // } - } return true; } // @ts-ignore @@ -15673,9 +15647,6 @@ function isColor(token, errors) { } return acc; }, [[]]); - // if (children.length === 0 || children[0].length === 0) { - // return false; - // } let j = 0; let k = 0; if (children[j][0].typ === exports.EnumToken.IdenTokenType && @@ -15709,50 +15680,13 @@ function isColor(token, errors) { k++; } } - // else { - // return false; - // } } else { k++; } } - // else { - // return false; - // } - // if (k != children[j].length) { - // return false; - // } j++; } - // while (j < children.length) { - // if (children[j].length > 2) { - // return false; - // } - // if ( - // !isColor(children[j][0]) && - // !( - // children[j][0].typ == EnumToken.WildCardFunctionTokenType && - // equalsIgnoreCase("calc", (children[j][0] as FunctionToken).val) - // ) - // ) { - // return false; - // } - // if (children[j][0].typ == EnumToken.WildCardFunctionTokenType) { - // const result = matchAllSyntaxes( - // getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, "calc()") as ValidationFunctionToken[], - // createValidationContext([children[j][0]]), - // {}, - // ); - // if (!result.success) { - // return false; - // } - // } - // if (children[j].length > 1 && !isPercentageToken(children[j][1])) { - // return false; - // } - // j++; - // } return true; } else { @@ -15765,21 +15699,6 @@ function isColor(token, errors) { // @ts-ignore for (const v of token.chi) { if (v.typ == exports.EnumToken.IdenTokenType) { - // if (isColor(v)) { - // continue; - // } - // if (!(keywords.includes(v.val) || COLORS_NAMES[v.val.toLowerCase()] != null)) { - // return false; - // } - // if (keywords.includes(v.val)) { - // if (isLegacySyntax) { - // return false; - // } - // // @ts-ignore - // if (v.val == "from" && ["rgba", "hsla"].includes((token as ColorToken).val)) { - // return false; - // } - // } continue; } if (v.typ === exports.EnumToken.MathFunctionTokenType || @@ -15787,20 +15706,6 @@ function isColor(token, errors) { colorsFunc.includes(v.val)) { continue; } - // if ( - // ![ - // EnumToken.ColorTokenType, - // EnumToken.IdenTokenType, - // EnumToken.NumberTokenType, - // EnumToken.AngleTokenType, - // EnumToken.PercentageTokenType, - // EnumToken.CommaTokenType, - // EnumToken.WhitespaceTokenType, - // EnumToken.LiteralTokenType, - // ].includes(v.typ) - // ) { - // return false; - // } } } return true; @@ -15856,63 +15761,12 @@ function parseColor(token) { } if (token.val == "color") { let index = token.chi.indexOf(tk); - // if ((token as ColorToken).cal == "rel") { - // for (let k = 0; k < (token as ColorToken).chi!.length; k++) { - // if (EnumToken.DashedIdenTokenType == (token as ColorToken).chi![k].typ) { - // index = k; - // break; - // } - // } - // } if (exports.EnumToken.DashedIdenTokenType == token?.chi?.[index]?.typ) { token.kin = exports.ColorType.CUSTOM_COLOR; } } } - // return token; } - // @ts-ignore - // token.typ = EnumToken.ColorTokenType; - // // @ts-ignore - // (token as ColorToken).kin = ColorType[token.val.replaceAll("-", "_").toUpperCase()]; - // if (!("chi" in token)) { - // const val: string = (token as ColorToken).val.toLowerCase(); - // if (val == "currentcolor" || val == "transparent" || val in COLORS_NAMES) { - // (token as ColorToken).kin = ColorType.LIT; - // } else if (isHexColor(val)) { - // (token as ColorToken).kin = ColorType.HEX; - // } - // const tk = (token as ColorToken).chi?.find( - // (t) => t.typ !== EnumToken.WhitespaceTokenType && t.typ !== EnumToken.CommentTokenType, - // ); - // if (tk?.typ === EnumToken.IdenTokenType && (tk as IdentToken).val === "from") { - // (token as ColorToken).cal = "rel"; - // } else if ((token as ColorToken).val == "color-mix" && (tk as IdentToken).val == "in") { - // (token as ColorToken).cal = "mix"; - // } else if ((token as ColorToken).val == "color") { - // (token as ColorToken).cal = "col"; - // } - // return token; - // } - // // @ts-ignore - // if (((token as ColorToken).chi as Token[])[0].typ == EnumToken.IdenTokenType) { - // // @ts-ignore - // if (((token as ColorToken).chi as Token[])[0].val == "from") { - // // @ts-ignore - // (token as ColorToken).cal = "rel"; - // } - // // @ts-ignore - // else if ((token as ColorToken).val == "color-mix" && ((token as ColorToken).chi as Token[])[0].val == "in") { - // // @ts-ignore - // (token as ColorToken).cal = "mix"; - // } else { - // // @ts-ignore - // if ((token as ColorToken).val == "color") { - // // @ts-ignore - // (token as ColorToken).cal = "col"; - // } - // } - // } return token; } function isLetter(codepoint) { @@ -16032,9 +15886,6 @@ function isHash(name) { return name.charAt(0) == "#" && isIdentStart(name.charCodeAt(1)); } const isNumber = memoize(function (name) { - // if (name.length == 0) { - // return false; - // } let codepoint = name.charCodeAt(0); let i = 0; const j = name.length; @@ -21412,6 +21263,9 @@ function substituteIfElseNode(declaration, node, wrapper, parentWrapper, cache) nam: left.val, chi: [], }); + if (declaration[PARENT] != null) { + atRule[LOC] = declaration[PARENT][LOC]; + } atRule[TOKENS] = [{ typ: exports.EnumToken.ParensTokenType, chi: left.chi.slice() }]; const minify = atRule.nam !== "supports"; const options = { @@ -21434,6 +21288,9 @@ function substituteIfElseNode(declaration, node, wrapper, parentWrapper, cache) }); atRule[TOKENS] = [left]; atRule.val = atRule[TOKENS].reduce((acc, curr) => acc + renderValue(curr), ""); + if (declaration[PARENT] != null) { + atRule[LOC] = declaration[PARENT][LOC]; + } clonedDeclaration = cloneNode(declaration, true, nodeMap); replaceNodeOrValue(nodeMap.get(targetWrapper.typ === exports.EnumToken.WildCardFunctionTokenType ? targetParentWrapper : targetWrapper), nodeMap.get(targetWrapper.typ === exports.EnumToken.WildCardFunctionTokenType ? targetWrapper : node), node.r.at(-1)?.typ === exports.EnumToken.SemiColonTokenType ? trimArray(node.r.slice(0, -1)) @@ -21667,12 +21524,13 @@ class SourceMap { return; } this.sourcesMap[this.sourcesMap.length] = id; - this.sources[this.sources.length] = fileName; - this.sourcesContent[this.sourcesContent.length] = content; + this.sources[this.sources.length] = fileName || null; + this.sourcesContent[this.sourcesContent.length] = content || null; } /** * Add all location * @param maps + * @throws */ addAll(maps) { let srcIndex; @@ -21697,12 +21555,7 @@ class SourceMap { } else { const arr = this.map.get(line); - record = [ - Math.max(0, newColumn - 1) - arr[0][0], - srcIndex - arr[0][1], - ln - 1, - col - 1, - ]; + record = [Math.max(0, newColumn - 1) - arr[0][0], srcIndex - arr[0][1], ln - 1, col - 1]; arr.push(record); } if (this.lastLocation != null) { @@ -21854,9 +21707,8 @@ class LineMap { if (offset < 0 || line < 0) { return [1, 1]; } - const column = offset - this.lineStarts[line] + 1; // [line, column] - return [line + 1, column == 0 ? 1 : column]; + return [line + 1, offset - this.lineStarts[line] + 1]; } /** * search the greatest index of the value less than or equal to offset @@ -22152,9 +22004,9 @@ function consumeString(parseInfo) { let value; let buffer = quote; const result = []; - while ((value = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset + 1))) { + while ((value = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset))) { if (value == "\\") { - if ("\\" == parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset + 2)) { + if ("\\" == parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset + 1)) { buffer += next(parseInfo, 2); continue; } @@ -22190,7 +22042,7 @@ function consumeString(parseInfo) { } next(parseInfo, escapeSequence.length + 1 + - (isWhiteSpace(parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset + 1)?.charCodeAt(0)) + (isWhiteSpace(parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset)?.charCodeAt(0)) ? 1 : 0)); continue; @@ -22347,12 +22199,12 @@ function yieldResult(val, parseInfo, hint) { end: parseInfo.currentPosition, }; parseInfo.position = parseInfo.currentPosition; - return { token, bytesIn: parseInfo.currentPosition + 1 }; + return { token, bytesIn: parseInfo.currentPosition }; } function match(parseInfo, input) { let position = parseInfo.currentPosition - parseInfo.offset; for (let i = 0; i < input.length; i++) { - if (parseInfo.stream[position + i + 1] != input.charAt(i)) { + if (parseInfo.stream[position + i] != input.charAt(i)) { return false; } } @@ -22360,14 +22212,14 @@ function match(parseInfo, input) { } function peek(parseInfo, count = 1) { if (count == 1) { - return parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset + 1); + return parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset); } const position = parseInfo.currentPosition - parseInfo.offset; - return parseInfo.stream.slice(position + 1, position + count + 1); + return parseInfo.stream.slice(position, position + count); } function next(parseInfo, count = 1) { let position = parseInfo.currentPosition - parseInfo.offset; - let char = count == 1 ? parseInfo.stream.charAt(position + 1) : parseInfo.stream.slice(position + 1, position + 1 + count); + let char = count == 1 ? parseInfo.stream.charAt(position) : parseInfo.stream.slice(position, position + count); let i = 0; let codepoint; for (; i < char.length; i++) { @@ -22403,7 +22255,7 @@ function tokenize(parseInfo, yieldEOFToken = true) { offset: 0, time: 0, position: 0, - currentPosition: -1, + currentPosition: 0, }; } let value; @@ -22495,7 +22347,7 @@ function tokenize(parseInfo, yieldEOFToken = true) { value !== ")" && value !== ""); } - if (values) { + if (values != null) { if (peek(parseInfo) === "") { for (let i = 0; i < values.length; i++) { values[i].token.typ = exports.EnumToken.BadUrlTokenType; @@ -22575,15 +22427,13 @@ function tokenize(parseInfo, yieldEOFToken = true) { buffer = ""; } buffer += next(parseInfo); - nextCharCode = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset + 1).charCodeAt(0); + nextCharCode = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset).charCodeAt(0); while (nextCharCode == 0x20 || (nextCharCode >= 0x9 && nextCharCode <= 0xd) || nextCharCode == 0x2028 || nextCharCode == 0x2029) { value += next(parseInfo); - nextCharCode = parseInfo.stream - .charAt(parseInfo.currentPosition - parseInfo.offset + 1) - .charCodeAt(0); + nextCharCode = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset).charCodeAt(0); } result.push(yieldResult(value, parseInfo, exports.EnumToken.WhitespaceTokenType)); buffer = ""; @@ -22776,7 +22626,7 @@ function tokenize(parseInfo, yieldEOFToken = true) { break; case 46 /* TokenMap.DOT */: const codepoint = parseInfo.stream - .charAt(parseInfo.currentPosition - parseInfo.offset + 2) + .charAt(parseInfo.currentPosition - parseInfo.offset + 1) .charCodeAt(0); if (!isDigit(codepoint) && buffer !== "") { result.push(yieldResult(buffer, parseInfo)); @@ -22822,10 +22672,10 @@ async function* tokenizeStream(input, parseInfo) { parseInfo.stream = stream; } else { - parseInfo.stream = (parseInfo.stream.slice(parseInfo.currentPosition - parseInfo.offset + 1) + + parseInfo.stream = (parseInfo.stream.slice(parseInfo.currentPosition - parseInfo.offset) + stream); } - parseInfo.offset = parseInfo.currentPosition + 1; + parseInfo.offset = parseInfo.currentPosition; } yield* tokenize(parseInfo, done); if (done) { @@ -24998,6 +24848,7 @@ function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, erro if ([exports.EnumToken.AtRuleNodeType, exports.EnumToken.KeyframesAtRuleNodeType].includes(data.typ) && !("chi" in data)) { return `${indent}@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val};`; } + const lineMapLength = linesMap ? linesMap.getLineStarts().length : 0; const prelude = (indent.length > 0 ? options.newLine : "") + indent + ([exports.EnumToken.AtRuleNodeType, exports.EnumToken.KeyframesAtRuleNodeType].includes(data.typ) @@ -25064,7 +24915,10 @@ function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, erro sourceLocation.end--; } if (options.removeEmpty && children === "") { - sourceLocation.end -= prelude.length; + if (sourcemaps != null) { + sourceLocation.end -= prelude.length; + linesMap.getLineStarts().length = lineMapLength; + } return ""; } const end = options.newLine + indent + `}`; @@ -30029,7 +29883,7 @@ async function doParse(iter, options = {}) { offset: 0, source, position: 0, - currentPosition: -1, + currentPosition: 0, }; const root = await doParse(stream instanceof ReadableStream ? tokenizeStream(stream, parseInfo) : tokenize(parseInfo), Object.assign({}, options, { minify: false, @@ -30339,7 +30193,7 @@ async function doParse(iter, options = {}) { time: 0, source, position: 0, - currentPosition: -1, + currentPosition: 0, }; const root = await doParse(stream instanceof ReadableStream ? tokenizeStream(stream, parseInfo) : tokenize(parseInfo), Object.assign({}, options, { source, @@ -30485,7 +30339,7 @@ async function doParse(iter, options = {}) { offset: 0, source: new SourceFile("", [], src.relative), position: 0, - currentPosition: -1, + currentPosition: 0, }) : tokenize({ stream, @@ -30493,7 +30347,7 @@ async function doParse(iter, options = {}) { offset: 0, position: 0, source: new SourceFile(stream, [], src.relative), - currentPosition: -1, + currentPosition: 0, }), Object.assign({}, options, { minify: false, setParent: false, @@ -31814,7 +31668,7 @@ async function parseDeclarations(declaration) { offset: 0, position: 0, source: new SourceFile(stream, [], ""), - currentPosition: -1, + currentPosition: 0, }), { setParent: false, minify: false, validation: false }).then((result) => { return result.ast.chi[0].chi.filter((t) => t.typ == exports.EnumToken.DeclarationNodeType || t.typ == exports.EnumToken.CommentNodeType); }); @@ -31849,7 +31703,7 @@ function parseString(src, options = { parseColor: true }, errors) { time: 0, source: new SourceFile(src, [], ""), position: 0, - currentPosition: -1, + currentPosition: 0, }; const tokenResults = tokenize(parseInfo); const mapped = []; @@ -32318,7 +32172,7 @@ function parseSync(...args) { time: 0, source: options.source, position: 0, - currentPosition: -1, + currentPosition: 0, }; const result = doParseSync(tokenize(options.parseInfo), options); return !options.module && !options.inputSourceMap && !options.sourcemap ? result : parseResult(result, options); @@ -32470,7 +32324,7 @@ async function parse(...args) { time: 0, source: options.source, position: 0, - currentPosition: -1, + currentPosition: 0, }; return doParse(stream instanceof ReadableStream ? tokenizeStream(stream, options.parseInfo) : tokenize(options.parseInfo), options).then((result) => (!options.module && !options.inputSourceMap ? result : parseResult(result, options))); } diff --git a/dist/index.d.ts b/dist/index.d.ts index e57f2de0..55ba8ce8 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -3975,6 +3975,7 @@ declare class SourceMap { /** * Add all location * @param maps + * @throws */ addAll(maps: Array<[number, number, number, number, number]>): void; /** diff --git a/dist/lib/ast/features/if.js b/dist/lib/ast/features/if.js index af655d3f..6977cf34 100644 --- a/dist/lib/ast/features/if.js +++ b/dist/lib/ast/features/if.js @@ -1,7 +1,7 @@ import { EnumToken } from '../types.js'; import { renderValue } from '../../renderer/render.js'; import { FeatureWalkMode } from './type.js'; -import { PARENT, TOKENS } from '../../syntax/constants.js'; +import { PARENT, LOC, TOKENS } from '../../syntax/constants.js'; import { equalsIgnoreCase } from '../../parser/utils/text.js'; import { replaceNodeOrValue } from '../../parser/utils/token.js'; import { cloneNode } from '../clone.js'; @@ -82,6 +82,9 @@ function substituteIfElseNode(declaration, node, wrapper, parentWrapper, cache) nam: left.val, chi: [], }); + if (declaration[PARENT] != null) { + atRule[LOC] = declaration[PARENT][LOC]; + } atRule[TOKENS] = [{ typ: EnumToken.ParensTokenType, chi: left.chi.slice() }]; const minify = atRule.nam !== "supports"; const options = { @@ -104,6 +107,9 @@ function substituteIfElseNode(declaration, node, wrapper, parentWrapper, cache) }); atRule[TOKENS] = [left]; atRule.val = atRule[TOKENS].reduce((acc, curr) => acc + renderValue(curr), ""); + if (declaration[PARENT] != null) { + atRule[LOC] = declaration[PARENT][LOC]; + } clonedDeclaration = cloneNode(declaration, true, nodeMap); replaceNodeOrValue(nodeMap.get(targetWrapper.typ === EnumToken.WildCardFunctionTokenType ? targetParentWrapper : targetWrapper), nodeMap.get(targetWrapper.typ === EnumToken.WildCardFunctionTokenType ? targetWrapper : node), node.r.at(-1)?.typ === EnumToken.SemiColonTokenType ? trimArray(node.r.slice(0, -1)) diff --git a/dist/lib/parser/linesmap.js b/dist/lib/parser/linesmap.js index 38c9fad1..e208ca1f 100644 --- a/dist/lib/parser/linesmap.js +++ b/dist/lib/parser/linesmap.js @@ -26,9 +26,8 @@ class LineMap { if (offset < 0 || line < 0) { return [1, 1]; } - const column = offset - this.lineStarts[line] + 1; // [line, column] - return [line + 1, column == 0 ? 1 : column]; + return [line + 1, offset - this.lineStarts[line] + 1]; } /** * search the greatest index of the value less than or equal to offset diff --git a/dist/lib/parser/parse.js b/dist/lib/parser/parse.js index 31bb400d..ac88964e 100644 --- a/dist/lib/parser/parse.js +++ b/dist/lib/parser/parse.js @@ -1499,7 +1499,7 @@ async function doParse(iter, options = {}) { offset: 0, source, position: 0, - currentPosition: -1, + currentPosition: 0, }; const root = await doParse(stream instanceof ReadableStream ? tokenizeStream(stream, parseInfo) : tokenize(parseInfo), Object.assign({}, options, { minify: false, @@ -1809,7 +1809,7 @@ async function doParse(iter, options = {}) { time: 0, source, position: 0, - currentPosition: -1, + currentPosition: 0, }; const root = await doParse(stream instanceof ReadableStream ? tokenizeStream(stream, parseInfo) : tokenize(parseInfo), Object.assign({}, options, { source, @@ -1955,7 +1955,7 @@ async function doParse(iter, options = {}) { offset: 0, source: new SourceFile("", [], src.relative), position: 0, - currentPosition: -1, + currentPosition: 0, }) : tokenize({ stream, @@ -1963,7 +1963,7 @@ async function doParse(iter, options = {}) { offset: 0, position: 0, source: new SourceFile(stream, [], src.relative), - currentPosition: -1, + currentPosition: 0, }), Object.assign({}, options, { minify: false, setParent: false, @@ -3284,7 +3284,7 @@ async function parseDeclarations(declaration) { offset: 0, position: 0, source: new SourceFile(stream, [], ""), - currentPosition: -1, + currentPosition: 0, }), { setParent: false, minify: false, validation: false }).then((result) => { return result.ast.chi[0].chi.filter((t) => t.typ == EnumToken.DeclarationNodeType || t.typ == EnumToken.CommentNodeType); }); @@ -3319,7 +3319,7 @@ function parseString(src, options = { parseColor: true }, errors) { time: 0, source: new SourceFile(src, [], ""), position: 0, - currentPosition: -1, + currentPosition: 0, }; const tokenResults = tokenize(parseInfo); const mapped = []; diff --git a/dist/lib/parser/tokenize.js b/dist/lib/parser/tokenize.js index 8a8593e0..e5b937aa 100644 --- a/dist/lib/parser/tokenize.js +++ b/dist/lib/parser/tokenize.js @@ -140,9 +140,9 @@ function consumeString(parseInfo) { let value; let buffer = quote; const result = []; - while ((value = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset + 1))) { + while ((value = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset))) { if (value == "\\") { - if ("\\" == parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset + 2)) { + if ("\\" == parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset + 1)) { buffer += next(parseInfo, 2); continue; } @@ -178,7 +178,7 @@ function consumeString(parseInfo) { } next(parseInfo, escapeSequence.length + 1 + - (isWhiteSpace(parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset + 1)?.charCodeAt(0)) + (isWhiteSpace(parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset)?.charCodeAt(0)) ? 1 : 0)); continue; @@ -335,12 +335,12 @@ function yieldResult(val, parseInfo, hint) { end: parseInfo.currentPosition, }; parseInfo.position = parseInfo.currentPosition; - return { token, bytesIn: parseInfo.currentPosition + 1 }; + return { token, bytesIn: parseInfo.currentPosition }; } function match(parseInfo, input) { let position = parseInfo.currentPosition - parseInfo.offset; for (let i = 0; i < input.length; i++) { - if (parseInfo.stream[position + i + 1] != input.charAt(i)) { + if (parseInfo.stream[position + i] != input.charAt(i)) { return false; } } @@ -348,14 +348,14 @@ function match(parseInfo, input) { } function peek(parseInfo, count = 1) { if (count == 1) { - return parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset + 1); + return parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset); } const position = parseInfo.currentPosition - parseInfo.offset; - return parseInfo.stream.slice(position + 1, position + count + 1); + return parseInfo.stream.slice(position, position + count); } function next(parseInfo, count = 1) { let position = parseInfo.currentPosition - parseInfo.offset; - let char = count == 1 ? parseInfo.stream.charAt(position + 1) : parseInfo.stream.slice(position + 1, position + 1 + count); + let char = count == 1 ? parseInfo.stream.charAt(position) : parseInfo.stream.slice(position, position + count); let i = 0; let codepoint; for (; i < char.length; i++) { @@ -391,7 +391,7 @@ function tokenize(parseInfo, yieldEOFToken = true) { offset: 0, time: 0, position: 0, - currentPosition: -1, + currentPosition: 0, }; } let value; @@ -483,7 +483,7 @@ function tokenize(parseInfo, yieldEOFToken = true) { value !== ")" && value !== ""); } - if (values) { + if (values != null) { if (peek(parseInfo) === "") { for (let i = 0; i < values.length; i++) { values[i].token.typ = EnumToken.BadUrlTokenType; @@ -563,15 +563,13 @@ function tokenize(parseInfo, yieldEOFToken = true) { buffer = ""; } buffer += next(parseInfo); - nextCharCode = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset + 1).charCodeAt(0); + nextCharCode = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset).charCodeAt(0); while (nextCharCode == 0x20 || (nextCharCode >= 0x9 && nextCharCode <= 0xd) || nextCharCode == 0x2028 || nextCharCode == 0x2029) { value += next(parseInfo); - nextCharCode = parseInfo.stream - .charAt(parseInfo.currentPosition - parseInfo.offset + 1) - .charCodeAt(0); + nextCharCode = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset).charCodeAt(0); } result.push(yieldResult(value, parseInfo, EnumToken.WhitespaceTokenType)); buffer = ""; @@ -764,7 +762,7 @@ function tokenize(parseInfo, yieldEOFToken = true) { break; case 46 /* TokenMap.DOT */: const codepoint = parseInfo.stream - .charAt(parseInfo.currentPosition - parseInfo.offset + 2) + .charAt(parseInfo.currentPosition - parseInfo.offset + 1) .charCodeAt(0); if (!isDigit(codepoint) && buffer !== "") { result.push(yieldResult(buffer, parseInfo)); @@ -810,10 +808,10 @@ async function* tokenizeStream(input, parseInfo) { parseInfo.stream = stream; } else { - parseInfo.stream = (parseInfo.stream.slice(parseInfo.currentPosition - parseInfo.offset + 1) + + parseInfo.stream = (parseInfo.stream.slice(parseInfo.currentPosition - parseInfo.offset) + stream); } - parseInfo.offset = parseInfo.currentPosition + 1; + parseInfo.offset = parseInfo.currentPosition; } yield* tokenize(parseInfo, done); if (done) { diff --git a/dist/lib/renderer/render.js b/dist/lib/renderer/render.js index de03e003..46e1f060 100644 --- a/dist/lib/renderer/render.js +++ b/dist/lib/renderer/render.js @@ -302,6 +302,7 @@ function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, erro if ([EnumToken.AtRuleNodeType, EnumToken.KeyframesAtRuleNodeType].includes(data.typ) && !("chi" in data)) { return `${indent}@${data.nam}${data.val === "" ? "" : options.indent || " "}${data.val};`; } + const lineMapLength = linesMap ? linesMap.getLineStarts().length : 0; const prelude = (indent.length > 0 ? options.newLine : "") + indent + ([EnumToken.AtRuleNodeType, EnumToken.KeyframesAtRuleNodeType].includes(data.typ) @@ -368,7 +369,10 @@ function renderAstNode(data, options, sourcemaps, sourceLocation, linesMap, erro sourceLocation.end--; } if (options.removeEmpty && children === "") { - sourceLocation.end -= prelude.length; + if (sourcemaps != null) { + sourceLocation.end -= prelude.length; + linesMap.getLineStarts().length = lineMapLength; + } return ""; } const end = options.newLine + indent + `}`; diff --git a/dist/lib/renderer/sourcemap/sourcemap.js b/dist/lib/renderer/sourcemap/sourcemap.js index 8d63f375..c628a0b2 100644 --- a/dist/lib/renderer/sourcemap/sourcemap.js +++ b/dist/lib/renderer/sourcemap/sourcemap.js @@ -84,12 +84,13 @@ class SourceMap { return; } this.sourcesMap[this.sourcesMap.length] = id; - this.sources[this.sources.length] = fileName; - this.sourcesContent[this.sourcesContent.length] = content; + this.sources[this.sources.length] = fileName || null; + this.sourcesContent[this.sourcesContent.length] = content || null; } /** * Add all location * @param maps + * @throws */ addAll(maps) { let srcIndex; @@ -114,12 +115,7 @@ class SourceMap { } else { const arr = this.map.get(line); - record = [ - Math.max(0, newColumn - 1) - arr[0][0], - srcIndex - arr[0][1], - ln - 1, - col - 1, - ]; + record = [Math.max(0, newColumn - 1) - arr[0][0], srcIndex - arr[0][1], ln - 1, col - 1]; arr.push(record); } if (this.lastLocation != null) { diff --git a/dist/lib/syntax/syntax.js b/dist/lib/syntax/syntax.js index 37ab53c3..6e83b1d0 100644 --- a/dist/lib/syntax/syntax.js +++ b/dist/lib/syntax/syntax.js @@ -591,7 +591,6 @@ function isColor(token, errors) { action: "drop", message: `Invalid color`, node: token, - // location: options.source!.getSourLocation(token[LOC]!.sta), }); return false; } @@ -614,31 +613,6 @@ function isColor(token, errors) { if (children.length == 4 || (isRelative && children.length == 6)) { return true; } - if (children.length == 8 || children.length == 6) { - children.at(-2); - children.at(-1); - // @ts-ignore - // if ( - // ((children.length > 6 || !isRelative) && sep.typ != EnumToken.LiteralTokenType) || - // (sep as LiteralToken).val != "/" - // ) { - // return false; - // } - // if (alpha.typ == EnumToken.IdenTokenType && (alpha as IdentToken).val != "none") { - // return false; - // } else { - // // @ts-ignore - // if (alpha.typ == EnumToken.PercentageTokenType) { - // if (+(alpha as PercentageToken).val < 0 || +(alpha as PercentageToken).val > 100) { - // return false; - // } - // } else if (alpha.typ == EnumToken.NumberTokenType) { - // if (+(alpha as NumberToken).val < 0 || +(alpha as NumberToken).val > 1) { - // return false; - // } - // } - // } - } return true; } // @ts-ignore @@ -655,9 +629,6 @@ function isColor(token, errors) { } return acc; }, [[]]); - // if (children.length === 0 || children[0].length === 0) { - // return false; - // } let j = 0; let k = 0; if (children[j][0].typ === EnumToken.IdenTokenType && @@ -691,50 +662,13 @@ function isColor(token, errors) { k++; } } - // else { - // return false; - // } } else { k++; } } - // else { - // return false; - // } - // if (k != children[j].length) { - // return false; - // } j++; } - // while (j < children.length) { - // if (children[j].length > 2) { - // return false; - // } - // if ( - // !isColor(children[j][0]) && - // !( - // children[j][0].typ == EnumToken.WildCardFunctionTokenType && - // equalsIgnoreCase("calc", (children[j][0] as FunctionToken).val) - // ) - // ) { - // return false; - // } - // if (children[j][0].typ == EnumToken.WildCardFunctionTokenType) { - // const result = matchAllSyntaxes( - // getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, "calc()") as ValidationFunctionToken[], - // createValidationContext([children[j][0]]), - // {}, - // ); - // if (!result.success) { - // return false; - // } - // } - // if (children[j].length > 1 && !isPercentageToken(children[j][1])) { - // return false; - // } - // j++; - // } return true; } else { @@ -747,21 +681,6 @@ function isColor(token, errors) { // @ts-ignore for (const v of token.chi) { if (v.typ == EnumToken.IdenTokenType) { - // if (isColor(v)) { - // continue; - // } - // if (!(keywords.includes(v.val) || COLORS_NAMES[v.val.toLowerCase()] != null)) { - // return false; - // } - // if (keywords.includes(v.val)) { - // if (isLegacySyntax) { - // return false; - // } - // // @ts-ignore - // if (v.val == "from" && ["rgba", "hsla"].includes((token as ColorToken).val)) { - // return false; - // } - // } continue; } if (v.typ === EnumToken.MathFunctionTokenType || @@ -769,20 +688,6 @@ function isColor(token, errors) { colorsFunc.includes(v.val)) { continue; } - // if ( - // ![ - // EnumToken.ColorTokenType, - // EnumToken.IdenTokenType, - // EnumToken.NumberTokenType, - // EnumToken.AngleTokenType, - // EnumToken.PercentageTokenType, - // EnumToken.CommaTokenType, - // EnumToken.WhitespaceTokenType, - // EnumToken.LiteralTokenType, - // ].includes(v.typ) - // ) { - // return false; - // } } } return true; @@ -838,63 +743,12 @@ function parseColor(token) { } if (token.val == "color") { let index = token.chi.indexOf(tk); - // if ((token as ColorToken).cal == "rel") { - // for (let k = 0; k < (token as ColorToken).chi!.length; k++) { - // if (EnumToken.DashedIdenTokenType == (token as ColorToken).chi![k].typ) { - // index = k; - // break; - // } - // } - // } if (EnumToken.DashedIdenTokenType == token?.chi?.[index]?.typ) { token.kin = ColorType.CUSTOM_COLOR; } } } - // return token; } - // @ts-ignore - // token.typ = EnumToken.ColorTokenType; - // // @ts-ignore - // (token as ColorToken).kin = ColorType[token.val.replaceAll("-", "_").toUpperCase()]; - // if (!("chi" in token)) { - // const val: string = (token as ColorToken).val.toLowerCase(); - // if (val == "currentcolor" || val == "transparent" || val in COLORS_NAMES) { - // (token as ColorToken).kin = ColorType.LIT; - // } else if (isHexColor(val)) { - // (token as ColorToken).kin = ColorType.HEX; - // } - // const tk = (token as ColorToken).chi?.find( - // (t) => t.typ !== EnumToken.WhitespaceTokenType && t.typ !== EnumToken.CommentTokenType, - // ); - // if (tk?.typ === EnumToken.IdenTokenType && (tk as IdentToken).val === "from") { - // (token as ColorToken).cal = "rel"; - // } else if ((token as ColorToken).val == "color-mix" && (tk as IdentToken).val == "in") { - // (token as ColorToken).cal = "mix"; - // } else if ((token as ColorToken).val == "color") { - // (token as ColorToken).cal = "col"; - // } - // return token; - // } - // // @ts-ignore - // if (((token as ColorToken).chi as Token[])[0].typ == EnumToken.IdenTokenType) { - // // @ts-ignore - // if (((token as ColorToken).chi as Token[])[0].val == "from") { - // // @ts-ignore - // (token as ColorToken).cal = "rel"; - // } - // // @ts-ignore - // else if ((token as ColorToken).val == "color-mix" && ((token as ColorToken).chi as Token[])[0].val == "in") { - // // @ts-ignore - // (token as ColorToken).cal = "mix"; - // } else { - // // @ts-ignore - // if ((token as ColorToken).val == "color") { - // // @ts-ignore - // (token as ColorToken).cal = "col"; - // } - // } - // } return token; } function isLetter(codepoint) { @@ -1014,9 +868,6 @@ function isHash(name) { return name.charAt(0) == "#" && isIdentStart(name.charCodeAt(1)); } const isNumber = memoize(function (name) { - // if (name.length == 0) { - // return false; - // } let codepoint = name.charCodeAt(0); let i = 0; const j = name.length; diff --git a/dist/node.js b/dist/node.js index 269a3bd5..02119cbb 100644 --- a/dist/node.js +++ b/dist/node.js @@ -188,7 +188,7 @@ function parseSync(...args) { time: 0, source: options.source, position: 0, - currentPosition: -1, + currentPosition: 0, }; const result = doParseSync(tokenize(options.parseInfo), options); return !options.module && !options.inputSourceMap && !options.sourcemap ? result : parseResult(result, options); @@ -340,7 +340,7 @@ async function parse(...args) { time: 0, source: options.source, position: 0, - currentPosition: -1, + currentPosition: 0, }; return doParse(stream instanceof ReadableStream ? tokenizeStream(stream, options.parseInfo) : tokenize(options.parseInfo), options).then((result) => (!options.module && !options.inputSourceMap ? result : parseResult(result, options))); } diff --git a/dist/web.js b/dist/web.js index eafdd719..ee36b442 100644 --- a/dist/web.js +++ b/dist/web.js @@ -182,7 +182,7 @@ function parseSync(...args) { time: 0, source: options.source, position: 0, - currentPosition: -1, + currentPosition: 0, }; const result = doParseSync(tokenize(options.parseInfo), options); return !options.module && !options.inputSourceMap && !options.sourcemap ? result : parseResult(result, options); @@ -318,7 +318,7 @@ async function parse(...args) { time: 0, source: options.source, position: 0, - currentPosition: -1, + currentPosition: 0, }; return doParse(stream instanceof ReadableStream ? tokenizeStream(stream, options.parseInfo) : tokenize(options.parseInfo), options).then((result) => (!options.module && !options.inputSourceMap ? result : parseResult(result, options))); } diff --git a/src/lib/ast/features/if.ts b/src/lib/ast/features/if.ts index 825bd9a0..d383349e 100644 --- a/src/lib/ast/features/if.ts +++ b/src/lib/ast/features/if.ts @@ -13,7 +13,7 @@ import type { import { EnumToken } from "../types.ts"; import { renderValue } from "../../renderer/render.ts"; import { FeatureWalkMode } from "./type.ts"; -import { PARENT, TOKENS } from "../../syntax/constants.ts"; +import { LOC, PARENT, TOKENS } from "../../syntax/constants.ts"; import { equalsIgnoreCase } from "../../parser/utils/text.ts"; import { replaceNodeOrValue } from "../../parser/utils/token.ts"; import { cloneNode } from "../../ast/clone.ts"; @@ -152,6 +152,10 @@ function substituteIfElseNode( chi: [] as Token[], }) as AstAtRule; + if (declaration[PARENT] != null) { + atRule[LOC] = declaration[PARENT][LOC]!; + } + atRule[TOKENS] = [{ typ: EnumToken.ParensTokenType, chi: (left as FunctionToken).chi.slice() }]; const minify: boolean = atRule.nam !== "supports"; @@ -185,6 +189,10 @@ function substituteIfElseNode( atRule[TOKENS] = [left]; atRule.val = atRule[TOKENS]!.reduce((acc: string, curr: Token) => acc + renderValue(curr), ""); + if (declaration[PARENT] != null) { + atRule[LOC] = declaration[PARENT][LOC]!; + } + clonedDeclaration = cloneNode(declaration, true, nodeMap) as AstDeclaration; replaceNodeOrValue( diff --git a/src/lib/parser/linesmap.ts b/src/lib/parser/linesmap.ts index 9356d0ea..f31f7196 100644 --- a/src/lib/parser/linesmap.ts +++ b/src/lib/parser/linesmap.ts @@ -31,9 +31,8 @@ export class LineMap { return [1, 1]; } - const column: number = offset - this.lineStarts[line] + 1; // [line, column] - return [line + 1, column == 0 ? 1 : column]; + return [line + 1, offset - this.lineStarts[line] + 1]; } /** diff --git a/src/lib/parser/parse.ts b/src/lib/parser/parse.ts index 9ed4dfc5..eb2373ba 100644 --- a/src/lib/parser/parse.ts +++ b/src/lib/parser/parse.ts @@ -1957,7 +1957,7 @@ export async function doParse( offset: 0, source, position: 0, - currentPosition: -1, + currentPosition: 0, } as ParseInfo; const root: ParseResult = await doParse( stream instanceof ReadableStream ? tokenizeStream(stream, parseInfo) : tokenize(parseInfo), @@ -2335,7 +2335,7 @@ export async function doParse( time: 0, source, position: 0, - currentPosition: -1, + currentPosition: 0, } as ParseInfo; const root: ParseResult = await doParse( @@ -2531,7 +2531,7 @@ export async function doParse( offset: 0, source: new SourceFile("", [], src.relative), position: 0, - currentPosition: -1, + currentPosition: 0, } as ParseInfo) : tokenize({ stream, @@ -2539,7 +2539,7 @@ export async function doParse( offset: 0, position: 0, source: new SourceFile(stream, [], src.relative), - currentPosition: -1, + currentPosition: 0, } as ParseInfo), Object.assign({}, options, { minify: false, @@ -4199,7 +4199,7 @@ export async function parseDeclarations(declaration: string): Promise { @@ -4243,7 +4243,7 @@ export function parseString( time: 0, source: new SourceFile(src, [], ""), position: 0, - currentPosition: -1, + currentPosition: 0, }; const tokenResults: TokenizeResult[] = tokenize(parseInfo); diff --git a/src/lib/parser/tokenize.ts b/src/lib/parser/tokenize.ts index 032df7af..6b517584 100644 --- a/src/lib/parser/tokenize.ts +++ b/src/lib/parser/tokenize.ts @@ -187,9 +187,9 @@ export function consumeString(parseInfo: ParseInfo): Array { const result: Array = []; - while ((value = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset + 1))) { + while ((value = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset))) { if (value == "\\") { - if ("\\" == parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset + 2)) { + if ("\\" == parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset + 1)) { buffer += next(parseInfo, 2); continue; } @@ -240,7 +240,7 @@ export function consumeString(parseInfo: ParseInfo): Array { escapeSequence.length + 1 + (isWhiteSpace( - parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset + 1)?.charCodeAt(0), + parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset)?.charCodeAt(0), ) ? 1 : 0), @@ -421,14 +421,14 @@ export function yieldResult(val: string, parseInfo: ParseInfo, hint?: EnumToken) parseInfo.position = parseInfo.currentPosition; - return { token, bytesIn: parseInfo.currentPosition + 1 }; + return { token, bytesIn: parseInfo.currentPosition }; } export function match(parseInfo: ParseInfo, input: string): boolean { let position: number = parseInfo.currentPosition - parseInfo.offset; for (let i: number = 0; i < input.length; i++) { - if (parseInfo.stream[position + i + 1] != input.charAt(i)) { + if (parseInfo.stream[position + i] != input.charAt(i)) { return false; } } @@ -438,18 +438,18 @@ export function match(parseInfo: ParseInfo, input: string): boolean { export function peek(parseInfo: ParseInfo, count: number = 1): string { if (count == 1) { - return parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset + 1); + return parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset); } const position = parseInfo.currentPosition - parseInfo.offset; - return parseInfo.stream.slice(position + 1, position + count + 1); + return parseInfo.stream.slice(position, position + count); } export function next(parseInfo: ParseInfo, count: number = 1): string { let position = parseInfo.currentPosition - parseInfo.offset; let char: string = - count == 1 ? parseInfo.stream.charAt(position + 1) : parseInfo.stream.slice(position + 1, position + 1 + count); + count == 1 ? parseInfo.stream.charAt(position) : parseInfo.stream.slice(position, position + count); let i: number = 0; let codepoint: number; @@ -491,7 +491,7 @@ export function tokenize(parseInfo: ParseInfo | string, yieldEOFToken: boolean = offset: 0, time: 0, position: 0, - currentPosition: -1, + currentPosition: 0, }; } @@ -603,32 +603,27 @@ export function tokenize(parseInfo: ParseInfo | string, yieldEOFToken: boolean = ); } - if (values) { - - if (peek(parseInfo) === "" ) { - + if (values != null) { + if (peek(parseInfo) === "") { for (let i = 0; i < values.length; i++) { - values[i].token.typ = EnumToken.BadUrlTokenType; } } result.push(...values); + } else if (buffer.length > 0) { + result.push( + yieldResult( + buffer.trimEnd(), + parseInfo, + // buffer.length > 0 + peek(parseInfo) === "" || !isURLToken(buffer) + ? EnumToken.BadUrlTokenType + : EnumToken.UrlTokenTokenType, + ), + ); + buffer = ""; } - - else if (buffer.length > 0) { - result.push( - yieldResult( - buffer.trimEnd(), - parseInfo, - // buffer.length > 0 - peek(parseInfo) === "" || !isURLToken(buffer) - ? EnumToken.BadUrlTokenType - : EnumToken.UrlTokenTokenType, - ), - ); - buffer = ""; - } } break; @@ -707,7 +702,7 @@ export function tokenize(parseInfo: ParseInfo | string, yieldEOFToken: boolean = } buffer += next(parseInfo); - nextCharCode = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset + 1).charCodeAt(0); + nextCharCode = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset).charCodeAt(0); while ( nextCharCode == 0x20 || @@ -716,9 +711,7 @@ export function tokenize(parseInfo: ParseInfo | string, yieldEOFToken: boolean = nextCharCode == 0x2029 ) { value += next(parseInfo); - nextCharCode = parseInfo.stream - .charAt(parseInfo.currentPosition - parseInfo.offset + 1) - .charCodeAt(0); + nextCharCode = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset).charCodeAt(0); } result.push(yieldResult(value, parseInfo, EnumToken.WhitespaceTokenType)); @@ -958,7 +951,7 @@ export function tokenize(parseInfo: ParseInfo | string, yieldEOFToken: boolean = case TokenMap.DOT: const codepoint = parseInfo.stream - .charAt(parseInfo.currentPosition - parseInfo.offset + 2) + .charAt(parseInfo.currentPosition - parseInfo.offset + 1) .charCodeAt(0); if (!isDigit(codepoint) && buffer !== "") { @@ -1015,11 +1008,11 @@ export async function* tokenizeStream( if (typeof parseInfo.stream != "string") { parseInfo.stream = stream as string; } else { - parseInfo.stream = (parseInfo.stream.slice(parseInfo.currentPosition - parseInfo.offset + 1) + + parseInfo.stream = (parseInfo.stream.slice(parseInfo.currentPosition - parseInfo.offset) + stream) as string; } - parseInfo.offset = parseInfo.currentPosition + 1; + parseInfo.offset = parseInfo.currentPosition; } yield* tokenize(parseInfo, done); diff --git a/src/lib/renderer/render.ts b/src/lib/renderer/render.ts index eac2907d..7f34434f 100644 --- a/src/lib/renderer/render.ts +++ b/src/lib/renderer/render.ts @@ -499,6 +499,7 @@ function renderAstNode( };`; } + const lineMapLength = linesMap ? linesMap.getLineStarts().length : 0; const prelude = (indent.length > 0 ? options.newLine : "") + indent + @@ -592,7 +593,10 @@ function renderAstNode( } if (options.removeEmpty && children === "") { - sourceLocation.end -= prelude.length; + if (sourcemaps != null) { + sourceLocation.end -= prelude.length; + linesMap!.getLineStarts().length = lineMapLength; + } return ""; } diff --git a/src/lib/renderer/sourcemap/sourcemap.ts b/src/lib/renderer/sourcemap/sourcemap.ts index 32149d20..3c9118d7 100644 --- a/src/lib/renderer/sourcemap/sourcemap.ts +++ b/src/lib/renderer/sourcemap/sourcemap.ts @@ -107,26 +107,24 @@ export class SourceMap { } addSourceContent(id: number, fileName: string | null, content: string | null): void { - if (this.sourcesMap.includes(id)) { return; } this.sourcesMap[this.sourcesMap.length] = id; - this.sources[this.sources.length] = fileName; - this.sourcesContent[this.sourcesContent.length] = content; + this.sources[this.sources.length] = fileName || null; + this.sourcesContent[this.sourcesContent.length] = content || null; } /** * Add all location * @param maps + * @throws */ addAll(maps: Array<[number, number, number, number, number]>): void { - let srcIndex: number; for (let [newLine, newColumn, srcId, ln, col] of maps) { const key = `${srcId}:${ln}:${col}:${newLine}:${newColumn}`; - const sourcemap = `${srcId}`; if (this.keys.has(key)) { continue; @@ -144,7 +142,6 @@ export class SourceMap { srcIndex = this.sourcesMap.indexOf(srcId); if (srcIndex == -1) { - throw new Error(`Source file ${srcId} not added to sourcemap`); } @@ -155,12 +152,7 @@ export class SourceMap { } else { const arr: number[][] = this.map.get(line) as number[][]; - record = [ - Math.max(0, newColumn - 1) - arr[0][0], - srcIndex - arr[0][1], - ln - 1, - col - 1, - ]; + record = [Math.max(0, newColumn - 1) - arr[0][0], srcIndex - arr[0][1], ln - 1, col - 1]; arr.push(record); } diff --git a/src/node.ts b/src/node.ts index 49fdddcc..bbb4dcb3 100644 --- a/src/node.ts +++ b/src/node.ts @@ -313,7 +313,7 @@ export function parseSync( time: 0, source: options.source, position: 0, - currentPosition: -1, + currentPosition: 0, } as ParseInfo; const result = doParseSync(tokenize(options.parseInfo), options) as ParseResult; @@ -655,7 +655,7 @@ export async function parse( time: 0, source: options.source, position: 0, - currentPosition: -1, + currentPosition: 0, } as ParseInfo; return doParse( diff --git a/src/web.ts b/src/web.ts index 69abccab..8569f18d 100644 --- a/src/web.ts +++ b/src/web.ts @@ -332,7 +332,7 @@ export function parseSync( time: 0, source: options.source, position: 0, - currentPosition: -1, + currentPosition: 0, } as ParseInfo; const result = doParseSync(tokenize(options.parseInfo), options); @@ -634,7 +634,7 @@ export async function parse( time: 0, source: options.source, position: 0, - currentPosition: -1, + currentPosition: 0, } as ParseInfo; return doParse( diff --git a/test/specs/code/sourcemaps.js b/test/specs/code/sourcemaps.js index 3080b219..e6f51c8e 100644 --- a/test/specs/code/sourcemaps.js +++ b/test/specs/code/sourcemaps.js @@ -36,6 +36,7 @@ button { `, beautify: true, sourcemap: "inline", + expandNestingRules: true, expandIfSyntax: true, resolveImport: true, output: "test/sourcemap.html", @@ -44,8 +45,8 @@ button { it("sourcemap unminified #1", async () => { return transform(options).then(async (result) => { result.map.computePositions(); - let positions = result.map.find(39, 3); - expect(positions?.length == 1 && positions[0].slice(0, 3)).deep.equals([null, 7, 3]); + let positions = result.map.find(40, 2); + expect(positions?.length == 1 && positions[0].slice(0, 3)).deep.equals([null, 6, 2]); }); }); @@ -53,13 +54,14 @@ button { return transform(options).then(async (result) => { const result2 = transformSync({ input: result.code, + nestingRules: false, sourcemap: "inline", output: "test/sourcemap.html", }); result2.map.computePositions(); - const positions = result2.map.find(1, 255); - expect(positions?.length == 1 && positions[0].slice(0, 3)).deep.equals([null, 23, 2]); + const positions = result2.map.find(1, 254); + expect(positions?.[0]?.slice?.(0, 3)).deep.equals([null, 19, 2]); }); }); }); From 8561e471c273e3e70a5e8697a9d6a0e3eb09516f Mon Sep 17 00:00:00 2001 From: Thierry Bela Nanga Date: Tue, 18 Aug 2026 20:32:51 -0400 Subject: [PATCH 14/22] improve code coverage #146 --- benchmark/package.json | 4 +- dist/index-umd-web.js | 61 +++++++++--------------- dist/index.cjs | 61 +++++++++--------------- dist/index.d.ts | 9 ---- dist/lib/parser/linesmap.js | 13 ++--- dist/lib/renderer/sourcemap/lib/codec.js | 6 +-- dist/lib/renderer/sourcemap/sourcemap.js | 22 ++++++--- dist/utils/sync.js | 20 +------- src/lib/parser/linesmap.ts | 14 ++---- src/lib/renderer/sourcemap/lib/codec.ts | 6 +-- src/lib/renderer/sourcemap/sourcemap.ts | 24 +++++++--- src/utils/sync.ts | 22 +-------- test/specs/code/sourcemaps.js | 32 +++++++++++++ 13 files changed, 127 insertions(+), 167 deletions(-) diff --git a/benchmark/package.json b/benchmark/package.json index dafde208..b243d652 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -11,7 +11,7 @@ }, "dependencies": { "@tbela99/css-parser": "^1.4.11", - "@tbela99/css-parser2": "github:tbela99/css-parser#2628ebce", + "@tbela99/css-parser2": "github:tbela99/css-parser#52223b9", "clean-css": "^5.3.3", "css-tree": "^3.2.1", "cssnano": "^8.0.6", @@ -19,6 +19,6 @@ "esbuild": "^0.28.2", "lightningcss": "^1.33.0", "postcss": "^8.5.26", - "vitest": "^4.1.10" + "vitest": "^4.1.11" } } \ No newline at end of file diff --git a/dist/index-umd-web.js b/dist/index-umd-web.js index 47223380..00fb8eab 100644 --- a/dist/index-umd-web.js +++ b/dist/index-umd-web.js @@ -21379,9 +21379,9 @@ let value = 0; for (let i = 0; i < str.length; i += 1) { let integer = char_to_integer[str[i]]; - if (integer === undefined) { - throw new Error('Invalid character (' + str[i] + ')'); - } + // if (integer === undefined) { + // throw new Error('Invalid character (' + str[i] + ')'); + // } const has_continuation_bit = integer & 32; integer &= 31; value += integer << shift; @@ -21494,6 +21494,22 @@ */ constructor(sourcemaps) { if (typeof sourcemaps === "string") { + if (sourcemaps.startsWith("data:")) { + let encoding = ""; + let offset = sourcemaps.indexOf(",") + 1; + if (offset == 0) { + offset = sourcemaps.lastIndexOf(";") + 1; + } + else { + encoding = sourcemaps.slice(sourcemaps.lastIndexOf(";") + 1, offset - 1); + } + if (encoding == "base64") { + sourcemaps = atob(sourcemaps.slice(offset)); + } + else { + sourcemaps = decodeURIComponent(sourcemaps.slice(offset)); + } + } sourcemaps = JSON.parse(sourcemaps); } if (sourcemaps != null) { @@ -21668,12 +21684,6 @@ mappings: mappings.join(";"), }; } - /** - * to string - */ - toString() { - return JSON.stringify(this); - } } /** @@ -21701,9 +21711,9 @@ */ getOffsets(offset) { const line = this.search(offset); - if (offset < 0 || line < 0) { - return [1, 1]; - } + // if (offset < 0 || line < 0) { + // return [1, 1]; + // } // [line, column] return [line + 1, offset - this.lineStarts[line] + 1]; } @@ -21743,13 +21753,6 @@ addLineStart(lineStart) { this.lineStarts.push(lineStart); } - /** - * clone the linemap - * @returns - */ - clone() { - return new LineMap(this.lineStarts.slice()); - } } /** @@ -31963,25 +31966,7 @@ const token = result.ast.chi.at(-1); if (token?.typ == exports.EnumToken.CommentTokenType && token.val.startsWith("/*# sourceMappingURL=")) { - const data = token.val.slice(21, -2).trim(); - let sourcemap; - let encoding = ""; - if (data.startsWith("data:")) { - let offset = data.indexOf(",") + 1; - if (offset == 0) { - offset = data.lastIndexOf(";") + 1; - } - else { - encoding = data.slice(data.lastIndexOf(";") + 1, offset - 1); - } - if (encoding == "base64") { - sourcemap = atob(data.slice(offset)); - } - else { - sourcemap = decodeURIComponent(data.slice(offset)); - } - options.source.setInputSourceMap(sourcemap); - } + options.source.setInputSourceMap(token.val.slice(21, -2).trim()); } } } diff --git a/dist/index.cjs b/dist/index.cjs index f24c74bd..053470c7 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -21382,9 +21382,9 @@ function decode(str) { let value = 0; for (let i = 0; i < str.length; i += 1) { let integer = char_to_integer[str[i]]; - if (integer === undefined) { - throw new Error('Invalid character (' + str[i] + ')'); - } + // if (integer === undefined) { + // throw new Error('Invalid character (' + str[i] + ')'); + // } const has_continuation_bit = integer & 32; integer &= 31; value += integer << shift; @@ -21497,6 +21497,22 @@ class SourceMap { */ constructor(sourcemaps) { if (typeof sourcemaps === "string") { + if (sourcemaps.startsWith("data:")) { + let encoding = ""; + let offset = sourcemaps.indexOf(",") + 1; + if (offset == 0) { + offset = sourcemaps.lastIndexOf(";") + 1; + } + else { + encoding = sourcemaps.slice(sourcemaps.lastIndexOf(";") + 1, offset - 1); + } + if (encoding == "base64") { + sourcemaps = atob(sourcemaps.slice(offset)); + } + else { + sourcemaps = decodeURIComponent(sourcemaps.slice(offset)); + } + } sourcemaps = JSON.parse(sourcemaps); } if (sourcemaps != null) { @@ -21671,12 +21687,6 @@ class SourceMap { mappings: mappings.join(";"), }; } - /** - * to string - */ - toString() { - return JSON.stringify(this); - } } /** @@ -21704,9 +21714,9 @@ class LineMap { */ getOffsets(offset) { const line = this.search(offset); - if (offset < 0 || line < 0) { - return [1, 1]; - } + // if (offset < 0 || line < 0) { + // return [1, 1]; + // } // [line, column] return [line + 1, offset - this.lineStarts[line] + 1]; } @@ -21746,13 +21756,6 @@ class LineMap { addLineStart(lineStart) { this.lineStarts.push(lineStart); } - /** - * clone the linemap - * @returns - */ - clone() { - return new LineMap(this.lineStarts.slice()); - } } /** @@ -31966,25 +31969,7 @@ function parseResult(result, options) { const token = result.ast.chi.at(-1); if (token?.typ == exports.EnumToken.CommentTokenType && token.val.startsWith("/*# sourceMappingURL=")) { - const data = token.val.slice(21, -2).trim(); - let sourcemap; - let encoding = ""; - if (data.startsWith("data:")) { - let offset = data.indexOf(",") + 1; - if (offset == 0) { - offset = data.lastIndexOf(";") + 1; - } - else { - encoding = data.slice(data.lastIndexOf(";") + 1, offset - 1); - } - if (encoding == "base64") { - sourcemap = atob(data.slice(offset)); - } - else { - sourcemap = decodeURIComponent(data.slice(offset)); - } - options.source.setInputSourceMap(sourcemap); - } + options.source.setInputSourceMap(token.val.slice(21, -2).trim()); } } } diff --git a/dist/index.d.ts b/dist/index.d.ts index 55ba8ce8..e228a16d 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -3996,10 +3996,6 @@ declare class SourceMap { * Convert to JSON object */ toJSON(): SourceMapObject; - /** - * to string - */ - toString(): string; } /** @@ -4036,11 +4032,6 @@ declare class LineMap { * add line start */ addLineStart(lineStart: number): void; - /** - * clone the linemap - * @returns - */ - clone(): LineMap; } /** diff --git a/dist/lib/parser/linesmap.js b/dist/lib/parser/linesmap.js index e208ca1f..54d8aec1 100644 --- a/dist/lib/parser/linesmap.js +++ b/dist/lib/parser/linesmap.js @@ -23,9 +23,9 @@ class LineMap { */ getOffsets(offset) { const line = this.search(offset); - if (offset < 0 || line < 0) { - return [1, 1]; - } + // if (offset < 0 || line < 0) { + // return [1, 1]; + // } // [line, column] return [line + 1, offset - this.lineStarts[line] + 1]; } @@ -65,13 +65,6 @@ class LineMap { addLineStart(lineStart) { this.lineStarts.push(lineStart); } - /** - * clone the linemap - * @returns - */ - clone() { - return new LineMap(this.lineStarts.slice()); - } } export { LineMap }; diff --git a/dist/lib/renderer/sourcemap/lib/codec.js b/dist/lib/renderer/sourcemap/lib/codec.js index ab152589..00fb734a 100644 --- a/dist/lib/renderer/sourcemap/lib/codec.js +++ b/dist/lib/renderer/sourcemap/lib/codec.js @@ -17,9 +17,9 @@ function decode(str) { let value = 0; for (let i = 0; i < str.length; i += 1) { let integer = char_to_integer[str[i]]; - if (integer === undefined) { - throw new Error('Invalid character (' + str[i] + ')'); - } + // if (integer === undefined) { + // throw new Error('Invalid character (' + str[i] + ')'); + // } const has_continuation_bit = integer & 32; integer &= 31; value += integer << shift; diff --git a/dist/lib/renderer/sourcemap/sourcemap.js b/dist/lib/renderer/sourcemap/sourcemap.js index c628a0b2..50fade39 100644 --- a/dist/lib/renderer/sourcemap/sourcemap.js +++ b/dist/lib/renderer/sourcemap/sourcemap.js @@ -57,6 +57,22 @@ class SourceMap { */ constructor(sourcemaps) { if (typeof sourcemaps === "string") { + if (sourcemaps.startsWith("data:")) { + let encoding = ""; + let offset = sourcemaps.indexOf(",") + 1; + if (offset == 0) { + offset = sourcemaps.lastIndexOf(";") + 1; + } + else { + encoding = sourcemaps.slice(sourcemaps.lastIndexOf(";") + 1, offset - 1); + } + if (encoding == "base64") { + sourcemaps = atob(sourcemaps.slice(offset)); + } + else { + sourcemaps = decodeURIComponent(sourcemaps.slice(offset)); + } + } sourcemaps = JSON.parse(sourcemaps); } if (sourcemaps != null) { @@ -231,12 +247,6 @@ class SourceMap { mappings: mappings.join(";"), }; } - /** - * to string - */ - toString() { - return JSON.stringify(this); - } } export { SourceMap }; diff --git a/dist/utils/sync.js b/dist/utils/sync.js index 6d8f258f..d249c12b 100644 --- a/dist/utils/sync.js +++ b/dist/utils/sync.js @@ -17,25 +17,7 @@ function parseResult(result, options) { const token = result.ast.chi.at(-1); if (token?.typ == EnumToken.CommentTokenType && token.val.startsWith("/*# sourceMappingURL=")) { - const data = token.val.slice(21, -2).trim(); - let sourcemap; - let encoding = ""; - if (data.startsWith("data:")) { - let offset = data.indexOf(",") + 1; - if (offset == 0) { - offset = data.lastIndexOf(";") + 1; - } - else { - encoding = data.slice(data.lastIndexOf(";") + 1, offset - 1); - } - if (encoding == "base64") { - sourcemap = atob(data.slice(offset)); - } - else { - sourcemap = decodeURIComponent(data.slice(offset)); - } - options.source.setInputSourceMap(sourcemap); - } + options.source.setInputSourceMap(token.val.slice(21, -2).trim()); } } } diff --git a/src/lib/parser/linesmap.ts b/src/lib/parser/linesmap.ts index f31f7196..219a10ca 100644 --- a/src/lib/parser/linesmap.ts +++ b/src/lib/parser/linesmap.ts @@ -27,9 +27,9 @@ export class LineMap { getOffsets(offset: number): [number, number] { const line: number = this.search(offset); - if (offset < 0 || line < 0) { - return [1, 1]; - } + // if (offset < 0 || line < 0) { + // return [1, 1]; + // } // [line, column] return [line + 1, offset - this.lineStarts[line] + 1]; @@ -74,12 +74,4 @@ export class LineMap { addLineStart(lineStart: number) { this.lineStarts.push(lineStart); } - - /** - * clone the linemap - * @returns - */ - clone(): LineMap { - return new LineMap(this.lineStarts.slice()); - } } diff --git a/src/lib/renderer/sourcemap/lib/codec.ts b/src/lib/renderer/sourcemap/lib/codec.ts index fd45e13a..8c06845c 100644 --- a/src/lib/renderer/sourcemap/lib/codec.ts +++ b/src/lib/renderer/sourcemap/lib/codec.ts @@ -22,9 +22,9 @@ export function decode(str: string) { for (let i = 0; i < str.length; i += 1) { let integer = char_to_integer[str[i]]; - if (integer === undefined) { - throw new Error('Invalid character (' + str[i] + ')'); - } + // if (integer === undefined) { + // throw new Error('Invalid character (' + str[i] + ')'); + // } const has_continuation_bit = integer & 32; diff --git a/src/lib/renderer/sourcemap/sourcemap.ts b/src/lib/renderer/sourcemap/sourcemap.ts index 3c9118d7..f71a6045 100644 --- a/src/lib/renderer/sourcemap/sourcemap.ts +++ b/src/lib/renderer/sourcemap/sourcemap.ts @@ -75,6 +75,23 @@ export class SourceMap { */ constructor(sourcemaps?: SourceMapObject | string) { if (typeof sourcemaps === "string") { + if (sourcemaps.startsWith("data:")) { + let encoding: string = ""; + let offset: number = sourcemaps.indexOf(",") + 1; + + if (offset == 0) { + offset = sourcemaps.lastIndexOf(";") + 1; + } else { + encoding = sourcemaps.slice(sourcemaps.lastIndexOf(";") + 1, offset - 1); + } + + if (encoding == "base64") { + sourcemaps = atob(sourcemaps.slice(offset)); + } else { + sourcemaps = decodeURIComponent(sourcemaps.slice(offset)); + } + } + sourcemaps = JSON.parse(sourcemaps) as SourceMapObject; } @@ -298,11 +315,4 @@ export class SourceMap { mappings: mappings.join(";"), }; } - - /** - * to string - */ - toString(): string { - return JSON.stringify(this); - } } diff --git a/src/utils/sync.ts b/src/utils/sync.ts index e21ea207..94a10fc3 100644 --- a/src/utils/sync.ts +++ b/src/utils/sync.ts @@ -20,27 +20,7 @@ export function parseResult(result: ParseResult, options: ParserOptions): ParseR token?.typ == EnumToken.CommentTokenType && (token as AstComment).val.startsWith("/*# sourceMappingURL=") ) { - const data = (token as AstComment).val.slice(21, -2).trim(); - let sourcemap: string; - let encoding: string = ""; - - if (data.startsWith("data:")) { - let offset: number = data.indexOf(",") + 1; - - if (offset == 0) { - offset = data.lastIndexOf(";") + 1; - } else { - encoding = data.slice(data.lastIndexOf(";") + 1, offset - 1); - } - - if (encoding == "base64") { - sourcemap = atob(data.slice(offset)); - } else { - sourcemap = decodeURIComponent(data.slice(offset)); - } - - options!.source!.setInputSourceMap(sourcemap); - } + options!.source!.setInputSourceMap((token as AstComment).val.slice(21, -2).trim()); } } } diff --git a/test/specs/code/sourcemaps.js b/test/specs/code/sourcemaps.js index e6f51c8e..9abbef59 100644 --- a/test/specs/code/sourcemaps.js +++ b/test/specs/code/sourcemaps.js @@ -64,5 +64,37 @@ button { expect(positions?.[0]?.slice?.(0, 3)).deep.equals([null, 19, 2]); }); }); + + it("input sourcemap minified #3", async () => { + return transform({...options, sourcemap: true}).then(async (result) => { + const result2 = transformSync({ + input: result.code, + nestingRules: false, + sourcemap: "inline", + inputSourceMap: result.map.toJSON(), + output: "test/sourcemap.html", + }); + + result2.map.computePositions(); + const positions = result2.map.find(1, 254); + expect(positions?.[0]?.slice?.(0, 3)).deep.equals([null, 19, 2]); + }); + }); + + it("input sourcemap minified #3", async () => { + return transform({...options, sourcemap: true}).then(async (result) => { + const result2 = transformSync({ + input: result.code, + nestingRules: false, + sourcemap: "inline", + inputSourceMap: `data:application/json;charset=utf-8;${encodeURIComponent(JSON.stringify(result.map.toJSON()))}`, + output: "test/sourcemap.html", + }); + + result2.map.computePositions(); + const positions = result2.map.find(1, 254); + expect(positions?.[0]?.slice?.(0, 3)).deep.equals([null, 19, 2]); + }); + }); }); } From d69f87451104129f9528a4a0ec27e8dd262c29e4 Mon Sep 17 00:00:00 2001 From: Thierry Bela Nanga Date: Tue, 18 Aug 2026 20:52:50 -0400 Subject: [PATCH 15/22] fix incorrect import path #146 --- src/utils/sync.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/sync.ts b/src/utils/sync.ts index 94a10fc3..43d04cd8 100644 --- a/src/utils/sync.ts +++ b/src/utils/sync.ts @@ -1,4 +1,4 @@ -import type { AstComment, ParseResult, ParserOptions, ParserSyncOptions } from "../@types/index.js"; +import type { AstComment, ParseResult, ParserOptions, ParserSyncOptions } from "../@types/index.d.ts"; import { EnumToken } from "../lib/ast/types.ts"; /** From c22c0f2297a4de568ecef5495fb6a9ecb79ca40e Mon Sep 17 00:00:00 2001 From: Thierry Bela Nanga Date: Tue, 18 Aug 2026 23:42:43 -0400 Subject: [PATCH 16/22] add function overload definitions #146 --- dist/index-umd-web.js | 31 ++++++++----- dist/index.cjs | 31 ++++++++----- dist/index.d.ts | 18 ++++++-- dist/lib/renderer/render.js | 2 +- dist/lib/renderer/sourcemap/sourcemap.js | 29 ++++++++---- dist/utils/sync.d.ts | 2 +- src/lib/renderer/render.ts | 2 +- src/lib/renderer/sourcemap/sourcemap.ts | 57 ++++++++++++++++++------ test/specs/code/sourcemaps.js | 20 ++++++--- 9 files changed, 136 insertions(+), 56 deletions(-) diff --git a/dist/index-umd-web.js b/dist/index-umd-web.js index 00fb8eab..37163965 100644 --- a/dist/index-umd-web.js +++ b/dist/index-umd-web.js @@ -21529,9 +21529,13 @@ this.computePositions(); } } - hasSourceContent(id) { - return this.sourcesMap.includes(id); - } + /** + * add source + * @param id + * @param fileName + * @param content + * @returns + */ addSourceContent(id, fileName, content) { if (this.sourcesMap.includes(id)) { return; @@ -21544,9 +21548,13 @@ * Add all location * @param maps * @throws + * @private */ - addAll(maps) { + add(...maps) { let srcIndex; + if (typeof maps[0] === "number") { + maps = [maps]; + } for (let [newLine, newColumn, srcId, ln, col] of maps) { const key = `${srcId}:${ln}:${col}:${newLine}:${newColumn}`; if (this.keys.has(key)) { @@ -21588,7 +21596,6 @@ let sourceFileIndex = 0; // second field let sourceCodeLine = 0; // third field let sourceCodeColumn = 0; // fourth field - let nameIndex = 0; // fifth field let generatedCodeColumn; let result; // mappings to original source @@ -21611,10 +21618,11 @@ sourceCodeLine += segment[2]; sourceCodeColumn += segment[3]; result.push(sourceFileIndex, sourceCodeLine, sourceCodeColumn); - if (segment.length === 5) { - nameIndex += segment[4]; - result.push(nameIndex); - } + // nameIndex not needed + // if (segment.length === 5) { + // nameIndex += segment[4]; + // result.push(nameIndex); + // } return result; }) .sort((a, b) => { @@ -21635,6 +21643,9 @@ * @param column generated column */ find(line, column) { + if (this.reverseMap.size == 0) { + this.computePositions(); + } if (!this.reverseMap.has(--line)) { return null; } @@ -24660,7 +24671,7 @@ source = options.sourcesMap.get(sourceId); sourcemap.addSourceContent(source.id, source.getFileName(), source.getContent()); } - sourcemap.addAll(sourcemaps.maps); + sourcemap.add(...sourcemaps.maps); result.map = sourcemap; if (options.sourcemap === "inline") { result.code += `\n/*# sourceMappingURL=${result.map.toUrl()} */`; diff --git a/dist/index.cjs b/dist/index.cjs index 053470c7..0f73af61 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -21532,9 +21532,13 @@ class SourceMap { this.computePositions(); } } - hasSourceContent(id) { - return this.sourcesMap.includes(id); - } + /** + * add source + * @param id + * @param fileName + * @param content + * @returns + */ addSourceContent(id, fileName, content) { if (this.sourcesMap.includes(id)) { return; @@ -21547,9 +21551,13 @@ class SourceMap { * Add all location * @param maps * @throws + * @private */ - addAll(maps) { + add(...maps) { let srcIndex; + if (typeof maps[0] === "number") { + maps = [maps]; + } for (let [newLine, newColumn, srcId, ln, col] of maps) { const key = `${srcId}:${ln}:${col}:${newLine}:${newColumn}`; if (this.keys.has(key)) { @@ -21591,7 +21599,6 @@ class SourceMap { let sourceFileIndex = 0; // second field let sourceCodeLine = 0; // third field let sourceCodeColumn = 0; // fourth field - let nameIndex = 0; // fifth field let generatedCodeColumn; let result; // mappings to original source @@ -21614,10 +21621,11 @@ class SourceMap { sourceCodeLine += segment[2]; sourceCodeColumn += segment[3]; result.push(sourceFileIndex, sourceCodeLine, sourceCodeColumn); - if (segment.length === 5) { - nameIndex += segment[4]; - result.push(nameIndex); - } + // nameIndex not needed + // if (segment.length === 5) { + // nameIndex += segment[4]; + // result.push(nameIndex); + // } return result; }) .sort((a, b) => { @@ -21638,6 +21646,9 @@ class SourceMap { * @param column generated column */ find(line, column) { + if (this.reverseMap.size == 0) { + this.computePositions(); + } if (!this.reverseMap.has(--line)) { return null; } @@ -24663,7 +24674,7 @@ function doRender(data, options = {}, mapping) { source = options.sourcesMap.get(sourceId); sourcemap.addSourceContent(source.id, source.getFileName(), source.getContent()); } - sourcemap.addAll(sourcemaps.maps); + sourcemap.add(...sourcemaps.maps); result.map = sourcemap; if (options.sourcemap === "inline") { result.code += `\n/*# sourceMappingURL=${result.map.toUrl()} */`; diff --git a/dist/index.d.ts b/dist/index.d.ts index e228a16d..5519e86f 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -3970,14 +3970,26 @@ declare class SourceMap { * @param sourcemaps */ constructor(sourcemaps: string | SourceMapObject); - hasSourceContent(id: number): boolean; + /** + * add source + * @param id + * @param fileName + * @param content + * @returns + */ addSourceContent(id: number, fileName: string | null, content: string | null): void; /** - * Add all location + * Add location + * @param maps + * @throws + */ + add(newLine: number, newColumn: number, srcId: number, ln: number, col: number): void; + /** + * Add multiple locations * @param maps * @throws */ - addAll(maps: Array<[number, number, number, number, number]>): void; + add(...maps: Array<[number, number, number, number, number]>): void; /** * compute original positions */ diff --git a/dist/lib/renderer/render.js b/dist/lib/renderer/render.js index 46e1f060..fe00492a 100644 --- a/dist/lib/renderer/render.js +++ b/dist/lib/renderer/render.js @@ -114,7 +114,7 @@ function doRender(data, options = {}, mapping) { source = options.sourcesMap.get(sourceId); sourcemap.addSourceContent(source.id, source.getFileName(), source.getContent()); } - sourcemap.addAll(sourcemaps.maps); + sourcemap.add(...sourcemaps.maps); result.map = sourcemap; if (options.sourcemap === "inline") { result.code += `\n/*# sourceMappingURL=${result.map.toUrl()} */`; diff --git a/dist/lib/renderer/sourcemap/sourcemap.js b/dist/lib/renderer/sourcemap/sourcemap.js index 50fade39..1c547f17 100644 --- a/dist/lib/renderer/sourcemap/sourcemap.js +++ b/dist/lib/renderer/sourcemap/sourcemap.js @@ -92,9 +92,13 @@ class SourceMap { this.computePositions(); } } - hasSourceContent(id) { - return this.sourcesMap.includes(id); - } + /** + * add source + * @param id + * @param fileName + * @param content + * @returns + */ addSourceContent(id, fileName, content) { if (this.sourcesMap.includes(id)) { return; @@ -107,9 +111,13 @@ class SourceMap { * Add all location * @param maps * @throws + * @private */ - addAll(maps) { + add(...maps) { let srcIndex; + if (typeof maps[0] === "number") { + maps = [maps]; + } for (let [newLine, newColumn, srcId, ln, col] of maps) { const key = `${srcId}:${ln}:${col}:${newLine}:${newColumn}`; if (this.keys.has(key)) { @@ -151,7 +159,6 @@ class SourceMap { let sourceFileIndex = 0; // second field let sourceCodeLine = 0; // third field let sourceCodeColumn = 0; // fourth field - let nameIndex = 0; // fifth field let generatedCodeColumn; let result; // mappings to original source @@ -174,10 +181,11 @@ class SourceMap { sourceCodeLine += segment[2]; sourceCodeColumn += segment[3]; result.push(sourceFileIndex, sourceCodeLine, sourceCodeColumn); - if (segment.length === 5) { - nameIndex += segment[4]; - result.push(nameIndex); - } + // nameIndex not needed + // if (segment.length === 5) { + // nameIndex += segment[4]; + // result.push(nameIndex); + // } return result; }) .sort((a, b) => { @@ -198,6 +206,9 @@ class SourceMap { * @param column generated column */ find(line, column) { + if (this.reverseMap.size == 0) { + this.computePositions(); + } if (!this.reverseMap.has(--line)) { return null; } diff --git a/dist/utils/sync.d.ts b/dist/utils/sync.d.ts index 0242b12d..5b81e527 100644 --- a/dist/utils/sync.d.ts +++ b/dist/utils/sync.d.ts @@ -1,4 +1,4 @@ -import type { ParseResult, ParserOptions, ParserSyncOptions } from "../@types/index.js"; +import type { ParseResult, ParserOptions, ParserSyncOptions } from "../@types/index.d.ts"; /** * parse result. process input sourcemap * @param result diff --git a/src/lib/renderer/render.ts b/src/lib/renderer/render.ts index 7f34434f..de4713f7 100644 --- a/src/lib/renderer/render.ts +++ b/src/lib/renderer/render.ts @@ -228,7 +228,7 @@ export function doRender( sourcemap.addSourceContent(source.id, source.getFileName(), source.getContent()); } - sourcemap.addAll(sourcemaps!.maps!); + sourcemap.add(...(sourcemaps!.maps! as Array<[number, number, number, number, number]>) ); result.map = sourcemap; if (options.sourcemap === "inline") { diff --git a/src/lib/renderer/sourcemap/sourcemap.ts b/src/lib/renderer/sourcemap/sourcemap.ts index f71a6045..fc57cecf 100644 --- a/src/lib/renderer/sourcemap/sourcemap.ts +++ b/src/lib/renderer/sourcemap/sourcemap.ts @@ -60,18 +60,17 @@ export class SourceMap { private line: number = -1; /** - * + * Constructor */ constructor(); /** - * + * Constructor * @param sourcemaps */ constructor(sourcemaps: string | SourceMapObject); /** * * @param sourcemaps - * @private */ constructor(sourcemaps?: SourceMapObject | string) { if (typeof sourcemaps === "string") { @@ -119,10 +118,13 @@ export class SourceMap { } } - hasSourceContent(id: number): boolean { - return this.sourcesMap.includes(id); - } - + /** + * add source + * @param id + * @param fileName + * @param content + * @returns + */ addSourceContent(id: number, fileName: string | null, content: string | null): void { if (this.sourcesMap.includes(id)) { return; @@ -133,14 +135,36 @@ export class SourceMap { this.sourcesContent[this.sourcesContent.length] = content || null; } + /** + * Add sourcemap + * @param newLine + * @param newColumn + * @param srcId + * @param ln + * @param col + */ + add(newLine: number, newColumn: number, srcId: number, ln: number, col: number): void; + + /** + * Add multiple sourcemaps + * @param maps + * @throws + */ + add(...maps: Array<[newLine: number, newColumn: number, srcId: number, ln: number, col: number]>): void; + /** * Add all location * @param maps * @throws */ - addAll(maps: Array<[number, number, number, number, number]>): void { + add(...maps: Array<[number, number, number, number, number]> | [number, number, number, number, number]): void { let srcIndex: number; - for (let [newLine, newColumn, srcId, ln, col] of maps) { + + if (typeof maps[0] === "number") { + maps = [maps as [number, number, number, number, number]]; + } + + for (let [newLine, newColumn, srcId, ln, col] of maps as Array<[number, number, number, number, number]>) { const key = `${srcId}:${ln}:${col}:${newLine}:${newColumn}`; if (this.keys.has(key)) { @@ -192,7 +216,7 @@ export class SourceMap { let sourceFileIndex: number = 0; // second field let sourceCodeLine: number = 0; // third field let sourceCodeColumn: number = 0; // fourth field - let nameIndex: number = 0; // fifth field + // let nameIndex: number = 0; // fifth field let generatedCodeColumn: number; let result: number[]; @@ -224,10 +248,11 @@ export class SourceMap { result.push(sourceFileIndex, sourceCodeLine, sourceCodeColumn); - if (segment.length === 5) { - nameIndex += segment[4]; - result.push(nameIndex); - } + // nameIndex not needed + // if (segment.length === 5) { + // nameIndex += segment[4]; + // result.push(nameIndex); + // } return result; }) @@ -253,6 +278,10 @@ export class SourceMap { * @param column generated column */ find(line: number, column: number): Array<[string | null, number, number, string | null]> | null { + if (this.reverseMap.size == 0) { + this.computePositions(); + } + if (!this.reverseMap.has(--line)) { return null; } diff --git a/test/specs/code/sourcemaps.js b/test/specs/code/sourcemaps.js index 9abbef59..939a2bdc 100644 --- a/test/specs/code/sourcemaps.js +++ b/test/specs/code/sourcemaps.js @@ -44,7 +44,7 @@ button { it("sourcemap unminified #1", async () => { return transform(options).then(async (result) => { - result.map.computePositions(); + // result.map.computePositions(); let positions = result.map.find(40, 2); expect(positions?.length == 1 && positions[0].slice(0, 3)).deep.equals([null, 6, 2]); }); @@ -59,14 +59,20 @@ button { output: "test/sourcemap.html", }); - result2.map.computePositions(); - const positions = result2.map.find(1, 254); + // result2.map.computePositions(); + let positions = result2.map.find(1, 254); expect(positions?.[0]?.slice?.(0, 3)).deep.equals([null, 19, 2]); + + positions = result2.map.find(1, 255); + expect(positions).equals(null); + + positions = result2.map.find(100, 255); + expect(positions).equals(null); }); }); it("input sourcemap minified #3", async () => { - return transform({...options, sourcemap: true}).then(async (result) => { + return transform({ ...options, sourcemap: true }).then(async (result) => { const result2 = transformSync({ input: result.code, nestingRules: false, @@ -75,14 +81,14 @@ button { output: "test/sourcemap.html", }); - result2.map.computePositions(); + // result2.map.computePositions(); const positions = result2.map.find(1, 254); expect(positions?.[0]?.slice?.(0, 3)).deep.equals([null, 19, 2]); }); }); it("input sourcemap minified #3", async () => { - return transform({...options, sourcemap: true}).then(async (result) => { + return transform({ ...options, sourcemap: true }).then(async (result) => { const result2 = transformSync({ input: result.code, nestingRules: false, @@ -91,7 +97,7 @@ button { output: "test/sourcemap.html", }); - result2.map.computePositions(); + // result2.map.computePositions(); const positions = result2.map.find(1, 254); expect(positions?.[0]?.slice?.(0, 3)).deep.equals([null, 19, 2]); }); From fb0f782d2dcb393ef4c368845b827e4d166e960f Mon Sep 17 00:00:00 2001 From: Thierry Bela Nanga Date: Wed, 19 Aug 2026 08:10:42 -0400 Subject: [PATCH 17/22] update documentation #146 --- files/assets/typedoc-custom.css | 5 ++ files/validation.md | 29 ++++++----- src/@types/ast.d.ts | 15 +++--- src/@types/token.d.ts | 8 +-- src/lib/ast/clone.ts | 11 ++-- src/lib/ast/features/calc.ts | 3 +- src/lib/ast/features/if.ts | 17 +++++-- src/lib/ast/find.ts | 7 +++ src/lib/ast/minify.ts | 28 ++++++++-- src/lib/ast/types.ts | 2 + src/lib/ast/walk.ts | 7 ++- src/lib/parser/parse.ts | 62 +++++++++++------------ src/lib/parser/utils/at-rule-container.ts | 3 +- src/lib/parser/utils/selector.ts | 5 +- src/lib/parser/utils/token.ts | 7 +++ src/lib/renderer/render.ts | 4 +- src/lib/syntax/color/alpha.ts | 4 +- src/lib/validation/match.ts | 3 ++ src/node.ts | 29 +++++------ src/utils/sync.ts | 11 +++- src/web.ts | 26 ++++------ 21 files changed, 179 insertions(+), 107 deletions(-) diff --git a/files/assets/typedoc-custom.css b/files/assets/typedoc-custom.css index 979dc2d2..2cb11e7a 100644 --- a/files/assets/typedoc-custom.css +++ b/files/assets/typedoc-custom.css @@ -92,3 +92,8 @@ html[data-theme="dark"] { text-align: center; } } + + +.tsd-kind-icon { + width: 16px; +} \ No newline at end of file diff --git a/files/validation.md b/files/validation.md index 665fe144..6ddd983b 100644 --- a/files/validation.md +++ b/files/validation.md @@ -70,24 +70,25 @@ The following example prints the validation state of each node, along with any v ```ts -import {EnumToken, EnumAstNodeStatus, transform} from "@tbela99/css-parser"; +import {EnumToken, EnumAstNodeStatus, transformSync, getNodeProperty} from "@tbela99/css-parser"; import type {TransformOptions, VisitorNodeMap, AstAtRule, AstRule, AstDeclaration} from "@tbela99/css-parser"; -const options: TransformOptions = { + +const options: TransformSyncOptions = { beautify: true, validation: true, visitor: { AtRule: (node: AstAtRule) => { - console.debug('>>> ' + node.nam, '\n state: ' +EnumAstNodeStatus[node.state], '\n errors:', node.errors) + console.debug('>>> ' + node.nam, '\n state: ' + EnumAstNodeStatus[getNodeProperty(node, 'state')], '\n errors:', getNodeProperty(node, 'errors')) }, Rule: (node: AstRule) => { - console.debug('>>> ' + node.sel, '\n state: ' + EnumAstNodeStatus[node.state], '\n errors:', node.errors) + console.debug('>>> ' + node.sel, '\n state: ' + EnumAstNodeStatus[getNodeProperty(node, 'state')], '\n errors:', getNodeProperty(node, 'errors')) }, Declaration: (node: AstDeclaration) => { - console.debug('>>> ' + node.nam, '\n state: ' + EnumAstNodeStatus[node.state], '\n errors:', node.errors) + console.debug('>>> ' + node.nam, '\n state: ' + EnumAstNodeStatus[getNodeProperty(node, 'state')], '\n errors:', getNodeProperty(node, 'errors')) } } as VisitorNodeMap }; @@ -112,7 +113,7 @@ const css = ` } `; -const result = await transform(css, options); +const result = transformSync(css, options); // > >>> .xl\:stats-horizontal:where([dir=rtl],[dir=rtl] *),.prose :where(tbody tr,thead):not(:where([class~=not-prose] *)),.prose :where(code):not(:where([class~=not-prose] *,pre *)) // > state: Validated @@ -161,7 +162,7 @@ const result = await transform(css, options); ## Overriding Node Validation State -A node's validation state can be overridden using node visitors. The `state` property is an [`EnumAstNodeStatus`](../enums/node.EnumAstNodeStatus.html) value. +A node's validation state can be overridden using node visitors. The `state` property type is [`EnumAstNodeStatus`](../enums/node.EnumAstNodeStatus.html) value. By default, nodes with any of the following states are discarded unless their state is overridden by a visitor: @@ -175,18 +176,20 @@ In the example below, the node's validation state is changed from `EnumAstNodeSt ```ts -import {EnumToken, EnumAstNodeStatus, transform} from "@tbela99/css-parser"; -import type {TransformOptions, VisitorNodeMap, AstDeclaration} from "@tbela99/css-parser"; -const options: TransformOptions = { +import {EnumToken, EnumAstNodeStatus, transformSync, getNodeProperty, setNodeProperty} from "@tbela99/css-parser"; +import type {TransformSyncOptions, VisitorNodeMap, AstDeclaration} from "@tbela99/css-parser"; + + +const options: TransformSyncOptions = { beautify: true, validation: true, visitor: { AtRule: (node: AstAtRule) => { - if (node.state == EnumAstNodeStatus.Invalid) { + if (getNodeProperty(node, "state") == EnumAstNodeStatus.Invalid) { - node.state = EnumAstNodeStatus.ValidationFailed; + setNodeProperty(node, "state", EnumAstNodeStatus.ValidationFailed); } }, } as VisitorNodeMap @@ -205,7 +208,7 @@ const css = ` } `; -const result = await transform(css, options); +const result = transformSync(css, options); console.debug(result.code); ``` diff --git a/src/@types/ast.d.ts b/src/@types/ast.d.ts index e018ba9d..39e6b399 100644 --- a/src/@types/ast.d.ts +++ b/src/@types/ast.d.ts @@ -1,6 +1,6 @@ import { EnumToken } from "../lib/ast/types.ts"; import { ERRORS, LOC, OPTIMIZED, PARENT, RAW, ROOT, STATE, TOKENS } from "../lib/syntax/constants.ts"; -import type { Token } from "./token.d.ts"; +import type { Token, CssVariableToken, CssVariableImportTokenType, WhitespaceToken } from "./token.d.ts"; /** * token or node location @@ -32,16 +32,16 @@ export declare interface BaseToken { * location info * @private */ - [LOC]?: SourceLocation; + [LOC]?: SourceLocation | null; /** * parent node * @private */ - [PARENT]?: AstNode; + [PARENT]?: AstNode | Token | null; /** * root node */ - [ROOT]?: AstStyleSheet; + [ROOT]?: AstStyleSheet | null; /** * prelude or selector tokens * @private @@ -51,12 +51,12 @@ export declare interface BaseToken { * node state * @private */ - [STATE]?: EnumAstNodeStatus; + [STATE]?: EnumAstNodeStatus | null; /** * node syntax errors * @private */ - [ERRORS]?: ErrorDescription[]; + [ERRORS]?: ErrorDescription[] | null; /** * property name * @private @@ -411,7 +411,8 @@ export declare type AstNode = | AstInvalidAtRule | AstInvalidDeclaration | CssVariableToken - | CssVariableImportTokenType; + | CssVariableImportTokenType + | WhitespaceToken; /** * token search result diff --git a/src/@types/token.d.ts b/src/@types/token.d.ts index 667ebd2e..f05ec4f4 100644 --- a/src/@types/token.d.ts +++ b/src/@types/token.d.ts @@ -223,10 +223,12 @@ export declare interface FunctionToken extends BaseToken { | EnumToken.ImageFunctionTokenType | EnumToken.TimelineFunctionTokenType | EnumToken.TimingFunctionTokenType - | EnumToken.ColorFunctionTokenType + | EnumToken.ColorTokenType | EnumToken.MathFunctionTokenType - | EnumToken.PseudoClassFunctionTokenType - | EnumToken.TransformFunctionTokenType; + | EnumToken.PseudoClassFuncTokenType + | EnumToken.TransformFunctionTokenType + | EnumToken.GeneralEnclosedFunctionTokenType + | EnumToken.WildCardFunctionTokenType; /** * function name */ diff --git a/src/lib/ast/clone.ts b/src/lib/ast/clone.ts index cba96bb2..c2fef793 100644 --- a/src/lib/ast/clone.ts +++ b/src/lib/ast/clone.ts @@ -22,25 +22,28 @@ export function cloneNode( for (const [name, value] of Object.entries(node)) { if (value == null || typeof value != "object") { + // @ts-ignore clone[name] = value; } else if (Array.isArray(value)) { + // @ts-ignore clone[name] = []; if (cloneChildren || name !== checkNode) { - for (const c of value) { const newObj = cloneNode(c, cloneChildren, cloneMap); - cloneMap?.set?.(c, newObj); - clone[name].push(newObj); + cloneMap?.set?.(c, newObj); + // @ts-ignore + clone[name].push(newObj); } } - } else { + // @ts-ignore clone[name] = { ...value }; } } for (const symbol of Object.getOwnPropertySymbols(node)) { + // @ts-ignore clone[symbol] = node[symbol]; } diff --git a/src/lib/ast/features/calc.ts b/src/lib/ast/features/calc.ts index 3f1405b7..c7709d31 100644 --- a/src/lib/ast/features/calc.ts +++ b/src/lib/ast/features/calc.ts @@ -160,7 +160,8 @@ export class ComputeCalcExpressionFeature { const children: Token[] = parent.typ == EnumToken.DeclarationNodeType ? (parent).val - : parent.chi; + : // @ts-ignore + parent.chi; if (values.length == 1 && values[0].typ != EnumToken.BinaryExpressionTokenType) { for (let i = 0; i < children.length; i++) { diff --git a/src/lib/ast/features/if.ts b/src/lib/ast/features/if.ts index d383349e..3d853b66 100644 --- a/src/lib/ast/features/if.ts +++ b/src/lib/ast/features/if.ts @@ -32,7 +32,7 @@ function substituteIfElseNode( parentWrapper: FunctionToken, cache: Set, ): AstNode[] { - const result: AstNode = []; + const result: AstNode[] = [] as AstNode[]; let nodeMap = new Map(); let clonedDeclaration; @@ -60,6 +60,7 @@ function substituteIfElseNode( : ((node as IfElseConditionToken).r as IfConditionToken).r, ); + // @ts-expect-error if (targetParentWrapper.typ != EnumToken.DeclarationNodeType) { let index: number = (targetParentWrapper as FunctionToken).chi.indexOf(targetWrapper); if (index != -1) { @@ -104,6 +105,7 @@ function substituteIfElseNode( ).r, ); + // @ts-ignore cache.add((siblingWrapper.chi[k] as IfElseConditionToken).l); } } @@ -140,6 +142,7 @@ function substituteIfElseNode( replaceNodeOrValue( nodeMap.get(parentWrapper), + // @ts-expect-error nodeMap.get(targetWrapper.typ === EnumToken.DeclarationNodeType ? node : targetWrapper), node.r.at(-1)?.typ === EnumToken.SemiColonTokenType ? trimArray(node.r.slice(0, -1)) : node.r, ); @@ -228,7 +231,7 @@ function processNode(declarationNode: AstDeclaration, cache: Set): AstN const { node: declaration, value: node } = findByValue(astNode, nodeMatcher) ?? {}; if (declaration == null || node == null) { - result.push(astNode); + result.push(astNode as Token); continue; } @@ -239,7 +242,9 @@ function processNode(declarationNode: AstDeclaration, cache: Set): AstN if (node!.node!.typ === EnumToken.WildCardFunctionTokenType) { for (i = 0; i < (node!.node as FunctionToken).chi.length; i++) { stack.push( + // @ts-expect-error ...substituteIfElseNode( + // @ts-expect-error declaration, (node!.node as FunctionToken).chi[i] as IfConditionToken | IfElseConditionToken, node!.node as FunctionToken, @@ -250,7 +255,9 @@ function processNode(declarationNode: AstDeclaration, cache: Set): AstN } } else { stack.push( + // @ts-expect-error ...substituteIfElseNode( + // @ts-expect-error declaration, node!.node as IfConditionToken, parentWrapper as FunctionToken, @@ -262,9 +269,11 @@ function processNode(declarationNode: AstDeclaration, cache: Set): AstN } if (result.length > 0) { + // @ts-expect-error replaceNodeOrValue(declarationNode[PARENT], declarationNode, result); } // else remove node? + // @ts-expect-error return result; } @@ -286,7 +295,7 @@ export class ExpandIfFeature { } } - run(declaration: AstDeclaration): AstNode | null { - return processNode(declaration, new Set()); + run(declaration: AstDeclaration): AstNode | AstNode[] | null { + return processNode(declaration, new Set()) as AstNode[]; } } diff --git a/src/lib/ast/find.ts b/src/lib/ast/find.ts index d283ced2..23bd1b84 100644 --- a/src/lib/ast/find.ts +++ b/src/lib/ast/find.ts @@ -98,6 +98,7 @@ export function findByValue( for (const { value, parent, root: rootNode, parents } of walkValues(source, node)) { if (matcher(value, node)) { + // @ts-ignore return { node, value: { node: value, parent, root: rootNode, parents } }; } } @@ -248,18 +249,24 @@ export function findValue( } else if (ast?.typ === EnumToken.DeclarationNodeType) { tokens = (ast as AstDeclaration).val; } else if (ast != null) { + // @ts-ignore if (matcher(ast, ast?.[PARENT])) { + // @ts-ignore return { node: ast, parent: ast?.[PARENT], root: null, parents: null }; } + // @ts-ignore if (Array.isArray(ast.chi)) { + // @ts-ignore tokens = ast.chi; } } if (Array.isArray(tokens)) { for (const { value, parent, root: rootNode, parents } of walkValues(tokens, ast)) { + // @ts-ignore if (matcher(value, parent, rootNode, parents)) { + // @ts-ignore return { node: value, parent, root: rootNode, parents }; } } diff --git a/src/lib/ast/minify.ts b/src/lib/ast/minify.ts index 3e1f51b7..d232bd67 100644 --- a/src/lib/ast/minify.ts +++ b/src/lib/ast/minify.ts @@ -141,13 +141,15 @@ export function minify( replacement[TOKENS] = parseString( replacement.typ == EnumToken.RuleNodeType || replacement.typ === EnumToken.KeyframesRuleNodeType ? replacement.sel - : replacement.nam, + : // @ts-ignore + replacement.nam, ); } const result = feature.run( replacement as AstRule | AstAtRule, options2, + // @ts-ignore parent[PARENT] ?? (ast as AstRule | AstAtRule | AstStyleSheet), context, FeatureWalkMode.Pre, @@ -164,10 +166,13 @@ export function minify( replacement != parent && parent[PARENT] != null ) { + // @ts-ignore replaceNodeOrValue(parent[PARENT] as AstRule | AstAtRule | AstStyleSheet, parent, replacement); } + // @ts-ignore if (replacement.chi != null) { + // @ts-ignore for (const node of replacement.chi) { node[PARENT] = replacement; parents.add(node as AstNode); @@ -205,6 +210,7 @@ export function minify( const result = feature.run( replacement as AstRule | AstAtRule, options2, + // @ts-ignore parent[PARENT] ?? (ast as AstRule | AstAtRule | AstStyleSheet), context, FeatureWalkMode.Post, @@ -226,7 +232,9 @@ export function minify( replaceNodeOrValue(parent[PARENT], parent, replacement); } + // @ts-ignore if (replacement.chi != null) { + // @ts-ignore for (const node of replacement.chi!) { node[PARENT] = replacement; parents.add(node as AstNode); @@ -262,6 +270,7 @@ function transformAtRuleMediaPrelude(values: Token[]) { // @ts-ignore values[values.indexOf(value)] = (value as MediaQueryConditionToken).l; } else { + // @ts-ignore replaceNodeOrValue(parent, value, (value as MediaQueryConditionToken).l); // @ts-ignore value = (value as MediaQueryConditionToken).l; @@ -338,8 +347,10 @@ function transformAtRuleMediaPrelude(values: Token[]) { const p = parents?.[parents?.indexOf?.(parent) + 1]; if (p != null) { + // @ts-ignore replaceNodeOrValue(p, parent, replacement); } else { + // @ts-ignore values.splice(values.indexOf(parent), 1, replacement as Token); } @@ -495,6 +506,7 @@ function doMinify( } while (previous?.typ === EnumToken.CommentNodeType) { + // @ts-ignore previous = ast.chi[--nodeIndex]; } @@ -525,6 +537,7 @@ function doMinify( // https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@keyframes#resolving_duplicates (previous).chi.push(...(node).chi); + // @ts-ignore ast.chi.splice(i, 1); previous = (ast?.chi?.[nodeIndex] as AstNode) ?? null; i = nodeIndex; @@ -624,6 +637,7 @@ function doMinify( (ast as AstAtRule).nam === (node as AstAtRule).nam && (ast as AstAtRule).val === (node as AstAtRule).val ) { + // @ts-ignore replaceNodeOrValue(ast as AstAtRule, node as AstAtRule, (node as AstAtRule).chi!); i--; continue; @@ -750,6 +764,7 @@ function doMinify( ")"; const sel2 = node[OPTIMIZED].selector.reduce( (acc: string, curr: string[]) => + // @ts-ignore (acc.length > 0 ? acc + "," : "") + node[OPTIMIZED].optimized[0] + curr.join(""), "", ); @@ -849,6 +864,7 @@ function doMinify( ")"; const sel2 = node[OPTIMIZED].selector.reduce( (acc: string, curr: string[]) => + // @ts-ignore (acc.length > 0 ? acc + "," : "") + node[OPTIMIZED].optimized[0] + curr.join(""), "", ); @@ -866,11 +882,14 @@ function doMinify( ); node[TOKENS] = null; } + // @ts-ignore } else if (node[OPTIMIZED]?.optimized.length > 0) { + // @ts-ignore const sel = node[OPTIMIZED].optimized.join(""); if (sel.length < node.sel.length) { node.sel = sel; + // @ts-ignore node[RAW] = [node[OPTIMIZED].optimized.slice()]; node[TOKENS] = null; } @@ -903,10 +922,13 @@ function doMinify( ((node.typ === EnumToken.RuleNodeType || node.typ === EnumToken.KeyframesRuleNodeType) && (node as AstRule).sel === (previous as AstRule).sel) || + // @ts-ignore (node.typ == EnumToken.AtRuleNodeType && (node as AstAtRule).nam !== "font-face" && + // @ts-ignore (node as AstAtRule).nam === (previous as AstAtRule).nam) ) { + // @ts-ignore node.chi.unshift(...previous.chi); doMinify(node, options, recursive, errors, nestingContent, context); @@ -1753,7 +1775,7 @@ function diff(n1: AstRule, n2: AstRule, options: ParserOptions = {}) { if (css == null) { let level: number = 0; - let parent: AstNode | null = curr[PARENT]; + let parent: AstNode | null = curr[PARENT] as AstNode; while (parent != null && parent.typ != EnumToken.StyleSheetNodeType) { level++; @@ -1776,7 +1798,7 @@ function diff(n1: AstRule, n2: AstRule, options: ParserOptions = {}) { } let level: number = 0; - let parent: AstNode | null = curr[PARENT]; + let parent: AstNode | null = curr[PARENT] as AstNode; while (parent != null && parent.typ != EnumToken.StyleSheetNodeType) { level++; diff --git a/src/lib/ast/types.ts b/src/lib/ast/types.ts index 8e9d4c6c..67a98ee3 100644 --- a/src/lib/ast/types.ts +++ b/src/lib/ast/types.ts @@ -48,6 +48,8 @@ export enum EnumAstNodeStatus { Malformed, } +export declare type AstNodePropertyType = "state" | "errors" | "location" | "tokens" | "parent" | "selector"; + /** * Enum of validation levels * @deprecated diff --git a/src/lib/ast/walk.ts b/src/lib/ast/walk.ts index b8e3bc52..df5c7825 100644 --- a/src/lib/ast/walk.ts +++ b/src/lib/ast/walk.ts @@ -334,13 +334,18 @@ export function* walk( if (includeValues) { if (node[TOKENS] != null) { + // @ts-ignore parents.splice(i, 0, ...(reverse ? node[TOKENS]!.toReversed() : node[TOKENS])); + // @ts-ignore } else if (Array.isArray(node.val)) { + // @ts-ignore parents.splice(i, 0, ...(reverse ? node.val.toReversed() : node.val)); } } + // @ts-ignore if (node["chi"] != null && (!isNumeric || ((option as number) & WalkerOptionEnum.IgnoreChildren) === 0)) { + // @ts-ignore parents.splice(i, 0, ...(reverse ? node.chi!.toReversed() : node.chi)); for (const child of (node).chi) { @@ -507,7 +512,7 @@ export function* walkValues( do { yield result; - next = map.get(result) ?? root; + next = map.get(result as Token) ?? root; if (next == result) { break; diff --git a/src/lib/parser/parse.ts b/src/lib/parser/parse.ts index eb2373ba..cd6980b2 100644 --- a/src/lib/parser/parse.ts +++ b/src/lib/parser/parse.ts @@ -866,7 +866,9 @@ export function doParseSync( } } + // @ts-ignore if (nodes[i].chi != null) { + // @ts-ignore subNodes.push(...nodes[i].chi); } @@ -1007,6 +1009,7 @@ export function doParseSync( } if (node != nodes[i]) { + // @ts-ignore replaceNodeOrValue(nodes[i][PARENT], nodes[i], node); } } @@ -1150,6 +1153,7 @@ export function doParseSync( ); } + // @ts-ignore if (node.typ == EnumToken.CssVariableDeclarationMapTokenType) { const from = (node as CssVariableMapTokenType).from.find( (t) => t.typ == EnumToken.IdenTokenType || isIdentColor(t), @@ -1502,6 +1506,7 @@ export function doParseSync( (value.typ == EnumToken.IdenTokenType || isIdentColor(value)) && (value as IdentToken).val in importedCssVariables ) { + // @ts-ignore replaceNodeOrValue(parent, value, importedCssVariables[(value as IdentToken).val].val); } } @@ -1609,7 +1614,7 @@ export function doParseSync( if (moduleSettings.scoped! & ModuleScopeEnumOptions.Pure) { if (!hasIdOrClass) { throw new Error( - `pure module: No id or class found in selector '${node.sel}' at '${node[LOC]?.src ?? ""}':${node[LOC]?.sta?.lin ?? ""}:${node[LOC]?.sta?.col ?? ""}`, + `pure module: No id or class found in selector '${node.sel}' at '${options.source!.getOffsets(node[LOC]?.sta as number).join(":")}'`, ); } } @@ -1972,7 +1977,8 @@ export async function doParse( stats.nodesCount += root.stats.nodesCount; stats.tokensCount += root.stats.tokensCount; stats.imports.push(root.stats); - node[PARENT]!.chi.splice(node[PARENT]!.chi.indexOf(node), 1, ...root.ast.chi); + // @ts-ignore + node[PARENT]!.chi!.splice(node[PARENT]!.chi!.indexOf(node), 1, ...root.ast.chi); if (root.errors.length > 0) { errors.push(...root.errors); @@ -2030,7 +2036,9 @@ export async function doParse( } } + // @ts-ignore if (nodes[i].chi != null) { + // @ts-ignore subNodes.push(...nodes[i].chi); } @@ -2176,7 +2184,8 @@ export async function doParse( } if (node != nodes[i]) { - replaceNodeOrValue(nodes[i][PARENT], nodes[i], node); + // @ts-ignore + replaceNodeOrValue(nodes[i]![PARENT] as AstNode, nodes[i], node); } } @@ -2355,6 +2364,7 @@ export async function doParse( continue; } + // @ts-ignore if (node.typ == EnumToken.CssVariableDeclarationMapTokenType) { const from = (node as CssVariableMapTokenType).from.find( (t) => t.typ == EnumToken.IdenTokenType || isIdentColor(t), @@ -2415,6 +2425,7 @@ export async function doParse( moduleSettings.pattern as string, moduleSettings.hashLength, ); + // @ts-ignore let value: string = result instanceof Promise ? await result : result; mapping[node.nam] = @@ -2815,33 +2826,17 @@ export async function doParse( for (const { value, parent } of walkValues(node.val, node)) { if (value.typ == EnumToken.DashedIdenTokenType) { - // if (!((value as DashedIdentToken).val in mapping)) { - // const result = - // moduleSettings.scoped! & ModuleScopeEnumOptions.Global - // ? (value as DashedIdentToken).val - // : moduleSettings.generateScopedName!( - // (value as DashedIdentToken).val, - // moduleSettings.filePath as string, - // moduleSettings.pattern as string, - // moduleSettings.hashLength, - // ); - // let val: string = result instanceof Promise ? await result : result; - - // mapping[(value as DashedIdentToken).val] = - // "--" + - // (moduleSettings.naming! & ModuleCaseTransformEnum.DashCaseOnly || - // moduleSettings.naming! & ModuleCaseTransformEnum.CamelCaseOnly - // ? getKeyName(val, moduleSettings.naming as ModuleCaseTransformEnum) - // : val); - // revMapping[mapping[(value as DashedIdentToken).val]] = (value as DashedIdentToken).val; - // } - (value as DashedIdentToken).val = mapping[(value as DashedIdentToken).val]; } else if ( (value.typ == EnumToken.IdenTokenType || isIdentColor(value)) && (value as IdentToken).val in importedCssVariables ) { - replaceNodeOrValue(parent, value, importedCssVariables[(value as IdentToken).val].val); + replaceNodeOrValue( + // @ts-ignore + parent as AstRule, + value, + importedCssVariables[(value as IdentToken).val].val, + ); } } } else if (node.typ == EnumToken.RuleNodeType) { @@ -2951,7 +2946,7 @@ export async function doParse( if (moduleSettings.scoped! & ModuleScopeEnumOptions.Pure) { if (!hasIdOrClass) { throw new Error( - `pure module: No id or class found in selector '${node.sel}' at '${node[LOC]?.src ?? ""}':${node[LOC]?.sta?.lin ?? ""}:${node[LOC]?.sta?.col ?? ""}`, + `pure module: No id or class found in selector '${node.sel}' at '${(options.source?.getOffsets?.(node[LOC]?.sta as number) ?? []).join(":")}'`, ); } } @@ -3142,7 +3137,9 @@ function parseNode( } if (i > 0) { - context.chi!.push(...(tokens.splice(0, i) as AstNode[]).filter((n) => n.typ !== EnumToken.WhitespaceTokenType)); + context.chi!.push( + ...(tokens.splice(0, i) as AstNode[]).filter((n: AstNode) => n.typ !== EnumToken.WhitespaceTokenType), + ); i = 0; } @@ -3198,7 +3195,7 @@ function parseNode( break; } - parent = parent[PARENT]; + parent = parent[PARENT] as AstNode; } node = parseAtRule( @@ -3277,7 +3274,7 @@ function parseNode( (node as AstNode)[STATE] == EnumAstNodeStatus.Unparsed || (node as AstNode)[STATE] == EnumAstNodeStatus.Malformed ) { - invalidNodes.push(node); + invalidNodes.push(node as AstDeclaration); } } } @@ -3735,8 +3732,8 @@ export function parseAtRule( let success: boolean = result.success; if (atRule.nam === "else") { - const siblings = (context as AstAtRule | AstStyleSheet).chi as AstNode[]; - let sibling: AstNode | null = null; + const siblings = (context as AstAtRule | AstStyleSheet).chi as AstNode[] | Token[]; + let sibling: AstNode | Token | null = null; let l: number = siblings.length; while (l--) { @@ -3757,9 +3754,12 @@ export function parseAtRule( if (sibling == null || sibling.typ !== EnumToken.AtRuleNodeType) { missingWhen = true; + // @ts-expect-error } else if (sibling.nam !== "when") { + // @ts-expect-error if (sibling.nam !== "else") { missingWhen = true; + // @ts-expect-error } else if (sibling.val === "") { definedAfterLastElse = true; } diff --git a/src/lib/parser/utils/at-rule-container.ts b/src/lib/parser/utils/at-rule-container.ts index e559713a..91a56293 100644 --- a/src/lib/parser/utils/at-rule-container.ts +++ b/src/lib/parser/utils/at-rule-container.ts @@ -193,7 +193,8 @@ export function parseAtRuleContainerQueryList( success = false; errors.push({ action: "drop", - node: options.source!.getSourceLocation(stream[i][LOC]!.sta), + node: stream[i], + location: options.source!.getSourceLocation(stream[i][LOC]!.sta), message: ` is not allowed outside of parentheses`, }); diff --git a/src/lib/parser/utils/selector.ts b/src/lib/parser/utils/selector.ts index feadd4ae..61a7bdbf 100644 --- a/src/lib/parser/utils/selector.ts +++ b/src/lib/parser/utils/selector.ts @@ -17,6 +17,7 @@ import type { PercentageToken, AtRuleToken, ColorToken, + AstNode, } from "../../../@types/index.d.ts"; import { EnumAstNodeStatus, EnumToken } from "../../ast/types.ts"; import { renderValue } from "../../renderer/render.ts"; @@ -164,12 +165,12 @@ export function parseSelector( do { if (parent?.typ === EnumToken.AtRuleNodeType && "media" === (parent as AstAtRule).nam) { - parent = parent[PARENT]; + parent = parent[PARENT] as AstRuleList; continue; } nested = parent?.typ == EnumToken.RuleNodeType; - parent = parent?.[PARENT]; + parent = parent?.[PARENT] as AstRuleList; } while (!nested && parent != null); for (; i < tokens.length; i++) { diff --git a/src/lib/parser/utils/token.ts b/src/lib/parser/utils/token.ts index 134340eb..27dac94e 100644 --- a/src/lib/parser/utils/token.ts +++ b/src/lib/parser/utils/token.ts @@ -43,6 +43,13 @@ export function replaceNodeOrValue( parent: | BinaryExpressionToken | (AstNode & + ( + | { chi: Token[] } + | { + val: Token[]; + } + )) + | (Token & ( | { chi: Token[] } | { diff --git a/src/lib/renderer/render.ts b/src/lib/renderer/render.ts index de4713f7..7e95e7cb 100644 --- a/src/lib/renderer/render.ts +++ b/src/lib/renderer/render.ts @@ -228,7 +228,7 @@ export function doRender( sourcemap.addSourceContent(source.id, source.getFileName(), source.getContent()); } - sourcemap.add(...(sourcemaps!.maps! as Array<[number, number, number, number, number]>) ); + sourcemap.add(...(sourcemaps!.maps! as Array<[number, number, number, number, number]>)); result.map = sourcemap; if (options.sourcemap === "inline") { @@ -581,7 +581,7 @@ function renderAstNode( sourceLocation.end - str.length + options.newLine!.length + indentSub.length, ), node[LOC]!.srcId, - ...source!.getOffsets(node[LOC].sta), + ...source!.getOffsets(node![LOC]!.sta), ]); } } diff --git a/src/lib/syntax/color/alpha.ts b/src/lib/syntax/color/alpha.ts index 51438b2f..2fd05fb2 100644 --- a/src/lib/syntax/color/alpha.ts +++ b/src/lib/syntax/color/alpha.ts @@ -49,7 +49,7 @@ export function alpha(color: ColorToken, alpha: Token): ColorToken | null { alpha.typ === EnumToken.MathFunctionTokenType && equalsIgnoreCase((alpha as FunctionToken).val, "calc") ) { - alpha = cloneNode(alpha, true); + alpha = cloneNode(alpha, true) as FunctionToken; const alphaValue = components[3] ?? { typ: EnumToken.NumberTokenType, @@ -58,7 +58,7 @@ export function alpha(color: ColorToken, alpha: Token): ColorToken | null { for (const { value, parent } of walkValues((alpha as FunctionToken).chi, alpha)) { if (value.typ === EnumToken.IdenTokenType && equalsIgnoreCase((value as IdentToken).val, "alpha")) { - replaceNodeOrValue(parent, value, alphaValue); + replaceNodeOrValue(parent as FunctionToken, value, alphaValue); } } diff --git a/src/lib/validation/match.ts b/src/lib/validation/match.ts index 9341109d..16a9879f 100644 --- a/src/lib/validation/match.ts +++ b/src/lib/validation/match.ts @@ -1325,6 +1325,7 @@ function matchSyntax( if ( tokensfuncDefMap.has(token.typ) && + // @ts-ignore (token as FunctionToken).typ === EnumToken.WildCardFunctionTokenDefType ) { const range = trimArray(context.peekRange()); @@ -2646,8 +2647,10 @@ function matchProperty( // ) { const newRange = range.map((t) => cloneNode(t, true)); + // @ts-ignore parseTokens(newRange, { parseColor: true }, errors); + // @ts-ignore success = newRange.length == 1 && isColor(newRange[0], errors); if (success) { diff --git a/src/node.ts b/src/node.ts index bbb4dcb3..471d6958 100644 --- a/src/node.ts +++ b/src/node.ts @@ -69,6 +69,7 @@ export type { ValidationToken } from "./lib/validation/parser/types.d.ts"; export { FeatureWalkMode } from "./lib/ast/features/type.ts"; export { dirname, resolve, ResponseType }; +export { getNodeProperty, setNodeProperty } from "./lib/ast/node.ts"; /** * Load file or url @@ -178,7 +179,7 @@ export function render( } /** - * Parse css file + * Parse CSS file * @param file url or path * @param options * @param asStream load file as stream @@ -210,7 +211,7 @@ export const parseFile = deprecate( ) as (file: string, options?: ParserOptions, asStream?: boolean) => Promise; /** - * Parse css string + * Parse CSS string * @param stream * @param options * @@ -230,7 +231,7 @@ export const parseFile = deprecate( export function parseSync(stream: string, options?: ParserSyncOptions): ParseResult; /** - * Parse css string + * Parse CSS string * @param options * * Parsing a string @@ -249,9 +250,8 @@ export function parseSync(stream: string, options?: ParserSyncOptions): ParseRes export function parseSync(options: ParseInputOptions & ParserSyncOptions): ParseResult; /** - * Parse css + * Parse CSS * @param args - * @private * * Parsing a string * @@ -321,7 +321,7 @@ export function parseSync( } /** - * Transform css + * Transform CSS * @param css * @param options * @@ -339,7 +339,7 @@ export function parseSync( export function transformSync(css: string, options?: TransformSyncOptions): TransformResult; /** - * Transform css + * Transform CSS * @param options * * ```ts @@ -355,7 +355,7 @@ export function transformSync(css: string, options?: TransformSyncOptions): Tran export function transformSync(options: ParseInputOptions & TransformSyncOptions): TransformResult; /** - * Transform css + * Transform CSS * * ```ts * @@ -367,7 +367,6 @@ export function transformSync(options: ParseInputOptions & TransformSyncOptions) * ``` * * @param args - * @private */ export function transformSync( ...args: [string, TransformSyncOptions?] | [ParseInputOptions & TransformSyncOptions] @@ -477,7 +476,7 @@ export function transformSync( export async function parse(stream: string | ReadableStream, options?: ParserOptions): Promise; /** - * Parse css + * Parse CSS * @param options * * @throws Error file not found @@ -511,7 +510,7 @@ export async function parse(stream: string | ReadableStream, options export async function parse(options: ParseInputFileOptions & ParserOptions): Promise; /** - * Parse css + * Parse CSS * @param options * * Parsing a string @@ -554,11 +553,10 @@ export async function parse(options: ParseInputFileOptions & ParserOptions): Pro export async function parse(options: ParseInputStreamOptions & ParserOptions): Promise; /** - * Parse css + * Parse CSS * @param args * * @throws Error file not found - * @private * * Parsing a string * @@ -748,7 +746,7 @@ export async function transform( ): Promise; /** - * Transform css + * Transform CSS * @param options * * Parsing a string @@ -810,7 +808,7 @@ export async function transform(options: ParseInputStreamOptions & TransformOpti export async function transform(options: ParseInputFileOptions & TransformOptions): Promise; /** - * Transform css + * Transform CSS * * Parsing a string * @@ -850,7 +848,6 @@ export async function transform(options: ParseInputFileOptions & TransformOption * console.log(result.code); * ``` * @param args - * @private */ export async function transform( ...args: diff --git a/src/utils/sync.ts b/src/utils/sync.ts index 43d04cd8..ac006a31 100644 --- a/src/utils/sync.ts +++ b/src/utils/sync.ts @@ -1,5 +1,6 @@ -import type { AstComment, ParseResult, ParserOptions, ParserSyncOptions } from "../@types/index.d.ts"; -import { EnumToken } from "../lib/ast/types.ts"; +import type { AstComment, AstNode, ParseResult, ParserOptions, ParserSyncOptions } from "../@types/index.d.ts"; +import { AstNodePropertyType, EnumToken } from "../lib/ast/types.ts"; +import { ERRORS, LOC, PARENT, STATE, TOKENS } from "../lib/syntax/constants.ts"; /** * parse result. process input sourcemap @@ -33,6 +34,12 @@ export function parseResult(result: ParseResult, options: ParserOptions): ParseR return result; } +/** + * + * @param options + * @param prefix + * @private + */ export function validateSyncArguments(options: ParserSyncOptions, prefix: string = "options."): void { const args = Object.entries(options); diff --git a/src/web.ts b/src/web.ts index 8569f18d..7a238d93 100644 --- a/src/web.ts +++ b/src/web.ts @@ -64,6 +64,7 @@ export type { ValidationToken } from "./lib/validation/parser/types.d.ts"; export { FeatureWalkMode } from "./lib/ast/features/type.ts"; export { dirname, resolve, ResponseType }; +export { getNodeProperty, setNodeProperty } from "./lib/ast/node.ts"; /** * Load file or url @@ -165,7 +166,7 @@ export function render( } /** - * Parse css file + * Parse CSS file * @param file url or path * @param options * @param asStream load file as stream @@ -200,7 +201,7 @@ export async function parseFile( } /** - * Parse css string + * Parse CSS string * @param stream * @param options * @@ -233,7 +234,7 @@ export async function parseFile( export function parseSync(stream: string, options?: ParserSyncOptions): ParseResult; /** - * Parse css string + * Parse CSS string * @param options * * Parsing a string @@ -265,9 +266,8 @@ export function parseSync(stream: string, options?: ParserSyncOptions): ParseRes export function parseSync(options: ParseInputOptions & ParserSyncOptions): ParseResult; /** - * Parse css + * Parse CSS * @param args - * @private * * Parsing a string * @@ -340,10 +340,9 @@ export function parseSync( } /** - * Transform css + * Transform CSS * @param css * @param options - * @private * * * ```ts @@ -359,7 +358,7 @@ export function parseSync( export function transformSync(css: string, options?: TransformSyncOptions): TransformResult; /** - * Transform css + * Transform CSS * @param options * * parsing a string @@ -389,7 +388,6 @@ export function transformSync(options: ParseInputOptions & TransformSyncOptions) * ``` * * @param args - * @private */ export function transformSync( ...args: [string, TransformSyncOptions?] | [ParseInputOptions & TransformSyncOptions] @@ -484,7 +482,7 @@ export function transformSync( export async function parse(stream: string | ReadableStream, options?: ParserOptions): Promise; /** - * Parse css + * Parse CSS * @param options * * @throws Error file not found @@ -517,7 +515,7 @@ export async function parse(stream: string | ReadableStream, options export async function parse(options: ParseInputFileOptions & ParserOptions): Promise; /** - * Parse css + * Parse CSS * @param options * * Parsing a string @@ -547,7 +545,7 @@ export async function parse(options: ParseInputFileOptions & ParserOptions): Pro export async function parse(options: ParseInputStreamOptions & ParserOptions): Promise; /** - * Parse css + * Parse CSS * * Example: * @@ -572,7 +570,6 @@ export async function parse(options: ParseInputStreamOptions & ParserOptions): P * console.log(result.ast); * ``` * @param args - * @private */ export async function parse( ...args: @@ -761,7 +758,7 @@ export async function transform(options: ParseInputStreamOptions & TransformOpti export async function transform(options: ParseInputFileOptions & TransformOptions): Promise; /** - * Transform css + * Transform CSS * * Example: * @@ -780,7 +777,6 @@ export async function transform(options: ParseInputFileOptions & TransformOption * console.log(result.code); * ``` * @param args - * @private */ export async function transform( ...args: From d7d6d641ca2f676fd94580516bb384405e548d98 Mon Sep 17 00:00:00 2001 From: Thierry Bela Nanga Date: Wed, 19 Aug 2026 23:28:59 -0400 Subject: [PATCH 18/22] eliminate buffer variable #146 --- dist/index-umd-web.js | 840 +++++++---- dist/index.cjs | 862 +++++++---- dist/index.d.ts | 140 +- dist/lib/ast/clone.js | 5 + dist/lib/ast/features/calc.js | 3 +- dist/lib/ast/features/if.js | 20 +- dist/lib/ast/find.js | 1 + dist/lib/ast/minify.js | 36 +- dist/lib/ast/walk.js | 5 + dist/lib/parser/declaration/map.js | 4 +- dist/lib/parser/parse.js | 41 +- dist/lib/parser/tokenize.js | 591 +++++--- dist/lib/parser/utils/at-rule-container.js | 3 +- dist/lib/parser/utils/selector.js | 24 + dist/lib/renderer/sourcemap/sourcemap.js | 3 +- dist/lib/syntax/syntax.js | 34 +- dist/lib/validation/match.js | 3 + dist/node.js | 35 +- dist/utils/sync.d.ts | 6 + dist/utils/sync.js | 6 + dist/web.js | 13 +- src/@types/parse.d.ts | 10 +- src/config.json | 1507 +++++++++++++++++++- src/lib/parser/declaration/map.ts | 8 +- src/lib/parser/parse.ts | 5 - src/lib/parser/tokenize.ts | 673 +++++---- src/lib/parser/utils/selector.ts | 36 + src/lib/syntax/syntax.ts | 5 + src/lib/validation/match.ts | 1 + src/node.ts | 26 +- test/inspect.js | 10 +- 31 files changed, 3747 insertions(+), 1209 deletions(-) diff --git a/dist/index-umd-web.js b/dist/index-umd-web.js index 37163965..c711e7b8 100644 --- a/dist/index-umd-web.js +++ b/dist/index-umd-web.js @@ -9550,13 +9550,18 @@ } if (includeValues) { if (node[TOKENS] != null) { + // @ts-ignore parents.splice(i, 0, ...(reverse ? node[TOKENS].toReversed() : node[TOKENS])); + // @ts-ignore } else if (Array.isArray(node.val)) { + // @ts-ignore parents.splice(i, 0, ...(reverse ? node.val.toReversed() : node.val)); } } + // @ts-ignore if (node["chi"] != null && (!isNumeric || (option & exports.WalkerOptionEnum.IgnoreChildren) === 0)) { + // @ts-ignore parents.splice(i, 0, ...(reverse ? node.chi.toReversed() : node.chi)); for (const child of node.chi) { map.set(child, node); @@ -10936,7 +10941,7 @@ [LOC]: pos, }; } - if (isPseudo(token)) { + if (isPseudo$1(token)) { return { typ: ValidationTokenEnum.PseudoClassToken, val: token, @@ -11693,23 +11698,28 @@ cloneMap?.set?.(node, clone); for (const [name, value] of Object.entries(node)) { if (value == null || typeof value != "object") { + // @ts-ignore clone[name] = value; } else if (Array.isArray(value)) { + // @ts-ignore clone[name] = []; if (cloneChildren || name !== checkNode) { for (const c of value) { const newObj = cloneNode(c, cloneChildren, cloneMap); cloneMap?.set?.(c, newObj); + // @ts-ignore clone[name].push(newObj); } } } else { + // @ts-ignore clone[name] = { ...value }; } } for (const symbol of Object.getOwnPropertySymbols(node)) { + // @ts-ignore clone[symbol] = node[symbol]; } return clone; @@ -12698,6 +12708,7 @@ return result; } if (tokensfuncDefMap.has(token.typ) && + // @ts-ignore token.typ === exports.EnumToken.WildCardFunctionTokenDefType) { const range = trimArray(context.peekRange()); result = matchSyntax(getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, token.val + "()")?.[0]?.chi, createValidationContext(range.slice(1, -1)), options); @@ -13698,7 +13709,9 @@ // ) // ) { const newRange = range.map((t) => cloneNode(t, true)); + // @ts-ignore parseTokens(newRange, { parseColor: true }, errors); + // @ts-ignore success = newRange.length == 1 && isColor(newRange[0], errors); if (success) { context.update(range.at(-1)); @@ -15809,6 +15822,9 @@ return false; } if (codepoint == REVERSE_SOLIDUS) { + if (i + 1 > j) { + return false; + } codepoint = name.charCodeAt(i + 1); // if (!isIdentCodepoint(codepoint)) { // return false; @@ -15845,36 +15861,7 @@ codepoint == 0x7f || (codepoint >= 0xe && codepoint <= 0x1f)); } - function isURLToken(str) { - let i = -1; - let c; - while (++i < str.length) { - c = str.charCodeAt(i); - // single quote or double quote or start parenthesis or close parenthesis - if (isNonPrintable(c) || c == 0x27 || c == 0x22 || c == 0x28 || c == 0x29) { - return false; - } - // valid escape - if (c == REVERSE_SOLIDUS) { - i++; - if (i >= str.length) { - return false; - } - c = str.charCodeAt(i); - // c is not '\n' or '\r' or '\f' - if (c == 0x6e || c == 0x72 || c == 0x66) { - return false; - } - continue; - } - // is white space - if (c == 0x20 || c == 0x09) { - break; - } - } - return i == str.length; - } - function isPseudo(name) { + function isPseudo$1(name) { return (name.charAt(0) == ":" && ((name.endsWith("(") && isIdent(name.charAt(1) == ":" ? name.slice(2, -1) : name.slice(1, -1))) || isIdent(name.charAt(1) == ":" ? name.slice(2) : name.slice(1)))); @@ -18881,7 +18868,9 @@ if (t.typ == exports.EnumToken.ImportantTokenType) { isImportant = true; } - if (filtered.length == 0 && t.typ != exports.EnumToken.WhitespaceTokenType && t.typ != exports.EnumToken.ImportantTokenType) { + if (filtered.length == 0 && + t.typ != exports.EnumToken.WhitespaceTokenType && + t.typ != exports.EnumToken.ImportantTokenType) { filtered.push(dec); } } @@ -19760,7 +19749,8 @@ // @ts-ignore const children = parent.typ == exports.EnumToken.DeclarationNodeType ? parent.val - : parent.chi; + : // @ts-ignore + parent.chi; if (values.length == 1 && values[0].typ != exports.EnumToken.BinaryExpressionTokenType) { for (let i = 0; i < children.length; i++) { if (children[i] == value) { @@ -21099,6 +21089,7 @@ } for (const { value, parent, root: rootNode, parents } of walkValues(source, node)) { if (matcher(value, node)) { + // @ts-ignore return { node, value: { node: value, parent, root: rootNode, parents } }; } } @@ -21207,6 +21198,7 @@ exports.EnumToken.SemiColonTokenType ? trimArray(node.r.r.slice(0, -1)) : node.r.r); + // @ts-expect-error if (targetParentWrapper.typ != exports.EnumToken.DeclarationNodeType) { let index = targetParentWrapper.chi.indexOf(targetWrapper); if (index != -1) { @@ -21228,6 +21220,7 @@ .r.r.slice(0, -1)) : siblingWrapper.chi[k] .r.r); + // @ts-ignore cache.add(siblingWrapper.chi[k].l); } } @@ -21251,7 +21244,9 @@ } if (left.typ === exports.EnumToken.IdenTokenType && equalsIgnoreCase("else", left.val)) { clonedDeclaration = cloneNode(declaration, true, nodeMap); - replaceNodeOrValue(nodeMap.get(parentWrapper), nodeMap.get(targetWrapper.typ === exports.EnumToken.DeclarationNodeType ? node : targetWrapper), node.r.at(-1)?.typ === exports.EnumToken.SemiColonTokenType ? trimArray(node.r.slice(0, -1)) : node.r); + replaceNodeOrValue(nodeMap.get(parentWrapper), + // @ts-expect-error + nodeMap.get(targetWrapper.typ === exports.EnumToken.DeclarationNodeType ? node : targetWrapper), node.r.at(-1)?.typ === exports.EnumToken.SemiColonTokenType ? trimArray(node.r.slice(0, -1)) : node.r); result.push(clonedDeclaration); } else if (left?.typ === exports.EnumToken.WhenElseFunctionTokenType) { @@ -21318,17 +21313,27 @@ const parentWrapper = node.parent ?? parents.find((node) => !nodeMatcher(node)); if (node.node.typ === exports.EnumToken.WildCardFunctionTokenType) { for (i = 0; i < node.node.chi.length; i++) { - stack.push(...substituteIfElseNode(declaration, node.node.chi[i], node.node, parentWrapper, cache)); + stack.push( + // @ts-expect-error + ...substituteIfElseNode( + // @ts-expect-error + declaration, node.node.chi[i], node.node, parentWrapper, cache)); } } else { - stack.push(...substituteIfElseNode(declaration, node.node, parentWrapper, parents[parents.indexOf(parentWrapper) + 1] ?? declaration, cache)); + stack.push( + // @ts-expect-error + ...substituteIfElseNode( + // @ts-expect-error + declaration, node.node, parentWrapper, parents[parents.indexOf(parentWrapper) + 1] ?? declaration, cache)); } } if (result.length > 0) { + // @ts-expect-error replaceNodeOrValue(declarationNode[PARENT], declarationNode, result); } // else remove node? + // @ts-expect-error return result; } class ExpandIfFeature { @@ -21490,7 +21495,6 @@ /** * * @param sourcemaps - * @private */ constructor(sourcemaps) { if (typeof sourcemaps === "string") { @@ -21548,7 +21552,6 @@ * Add all location * @param maps * @throws - * @private */ add(...maps) { let srcIndex; @@ -21596,6 +21599,7 @@ let sourceFileIndex = 0; // second field let sourceCodeLine = 0; // third field let sourceCodeColumn = 0; // fourth field + // let nameIndex: number = 0; // fifth field let generatedCodeColumn; let result; // mappings to original source @@ -22011,17 +22015,17 @@ TokenMap[TokenMap["GREATERTHAN"] = 62] = "GREATERTHAN"; })(TokenMap || (TokenMap = {})); function consumeString(parseInfo) { - const quote = next(parseInfo); - let value; - let buffer = quote; + const quote = next(parseInfo).charCodeAt(0); + let charCode; + let decodeSegments = false; const result = []; - while ((value = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset))) { - if (value == "\\") { - if ("\\" == parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset + 1)) { - buffer += next(parseInfo, 2); + while ((charCode = parseInfo.stream.charCodeAt(parseInfo.currentPosition - parseInfo.offset)) == charCode) { + if (charCode == 92 /* TokenMap.REVERSE_SOLIDUS */) { + if (charCode == parseInfo.stream.charCodeAt(parseInfo.currentPosition - parseInfo.offset + 1)) { + next(parseInfo, 2); continue; } - const sequence = peek(parseInfo, 6); + const sequence = peek(parseInfo, 7); let escapeSequence = ""; let codepoint; let i; @@ -22040,50 +22044,72 @@ break; } if (escapeSequence.trimEnd().length > 0) { - const codepoint = parseInt(escapeSequence, 16); - if (codepoint == 0 || - // leading surrogate - (0xd800 <= codepoint && codepoint <= 0xdbff) || - // trailing surrogate - (0xdc00 <= codepoint && codepoint <= 0xdfff)) { - buffer += String.fromCodePoint(0xfffd); - } - else { - buffer += String.fromCodePoint(codepoint); - } - next(parseInfo, escapeSequence.length + + // const codepoint = parseInt(escapeSequence, 16); + // TODO set decode flag ON + // if ( + // codepoint == 0 || + // // leading surrogate + // (0xd800 <= codepoint && codepoint <= 0xdbff) || + // // trailing surrogate + // (0xdc00 <= codepoint && codepoint <= 0xdfff) + // ) { + // buffer += String.fromCodePoint(0xfffd); + // } else { + // buffer += String.fromCodePoint(codepoint); + // } + const length = escapeSequence.length + 1 + (isWhiteSpace(parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset)?.charCodeAt(0)) ? 1 - : 0)); + : 0); + decodeSegments = true; + next(parseInfo, length); continue; } - buffer += next(parseInfo, 2); + next(parseInfo, 2); continue; } - if (value == quote) { - buffer += value; - result.push(yieldResult(buffer, parseInfo, - /* hasNewLine ? EnumToken.BadStringTokenType : */ exports.EnumToken.StringTokenType)); + if (charCode == quote) { next(parseInfo); - buffer = ""; + result.push(yieldResult(parseInfo, + /* hasNewLine ? EnumToken.BadStringTokenType : */ exports.EnumToken.StringTokenType, decodeSegments ? { decodeSegments } : null)); return result; } - if (isNewLine(value.charCodeAt(0))) { - result.push(yieldResult(buffer + next(parseInfo), parseInfo, exports.EnumToken.BadStringTokenType)); + if (isNewLine(charCode)) { + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.BadStringTokenType)); return result; } - buffer += value; next(parseInfo); } // EOF - 'Unclosed-string' fixed - result.push(yieldResult(buffer + quote, parseInfo, exports.EnumToken.StringTokenType)); + result.push(yieldResult(parseInfo, exports.EnumToken.StringTokenType)); return result; } - function yieldResult(val, parseInfo, hint) { + function yieldResult(parseInfo, hint, options) { + let val = parseInfo.stream.slice(parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset); let token = null; let dimension; - // console.debug(`Yield result: ${val}, ${hint}`); + // if (val === "" && hint != EnumToken.EOFTokenType) { + // console.error(parseInfo.stream.length > parseInfo.currentPosition - parseInfo.offset, parseInfo.position < parseInfo.currentPosition, parseInfo.currentPosition - parseInfo.offset, parseInfo.stream.slice(parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset), parseInfo.stream.length, parseInfo.currentPosition- parseInfo.offset, parseInfo.position- parseInfo.offset, parseInfo.position, parseInfo.currentPosition, parseInfo.offset) + // console.error(new Error(`val is empty '${hint}'`)); + // } + // console.error({val, hint, position: parseInfo.position - parseInfo.offset, currentPosition: parseInfo.currentPosition - parseInfo.offset, endPosition: parseInfo.stream.length, offset: parseInfo.offset, len: parseInfo.stream.length}); + // console.error(new Error('incomplete token')); + if (options?.decodeSegments) { + val = val.replace(/\\([0-9a-fA-F]{1,6})(?:\s)?/g, (_, sequence) => { + const codepoint = parseInt(sequence, 16); + if (codepoint == 0 || + // leading surrogate + (0xd800 <= codepoint && codepoint <= 0xdbff) || + // trailing surrogate + (0xdc00 <= codepoint && codepoint <= 0xdfff) || + codepoint > 0x10ffff) { + return "\uFFFD"; + } + return String.fromCodePoint(codepoint); + }); + } if (hint != null) { let searchArray = null; switch (hint) { @@ -22252,6 +22278,127 @@ parseInfo.currentPosition += char.length; return char; } + function isIdentToken(parseInfo, start, end) { + let j = parseInfo.currentPosition - parseInfo.offset; + let i = parseInfo.position - parseInfo.offset; + if (start != null) { + if (end == null) { + if (start < 0) { + j += start; + } + else { + i += start; + } + } + else { + if (end < 0) { + j += end; + } + else { + j = parseInfo.position + end; + } + } + } + j--; + let codepoint = parseInfo.stream.charCodeAt(i); + // - + if (codepoint == 0x2d) { + let nextCodepoint; + if ((nextCodepoint = parseInfo.stream.charCodeAt(i + 1)) != nextCodepoint) { + return false; + } + // if (nextCodepoint == REVERSE_SOLIDUS) { + // return name.length > 2 && !isNewLine(name.charCodeAt(2) as number); + // } + if (isDigit(nextCodepoint)) { + return false; + } + codepoint = nextCodepoint; + i++; + } + if (codepoint !== 0x2d && !isIdentStart(codepoint)) { + return false; + } + if (codepoint == 92 /* TokenMap.REVERSE_SOLIDUS */) { + codepoint = parseInfo.stream.charCodeAt(i + 1); + // if (!isIdentCodepoint(codepoint)) { + // return false; + // } + i += String.fromCodePoint(codepoint).length; + // if (i < j) { + // codepoint = name.charCodeAt(i) as number; + // if (!isIdentCodepoint(codepoint)) { + // return false; + // } + // } + } + while (i < j) { + i += codepoint < 0x80 ? 1 : String.fromCodePoint(codepoint).length; + codepoint = parseInfo.stream.charCodeAt(i); + if (codepoint == 92 /* TokenMap.REVERSE_SOLIDUS */) { + i += codepoint < 0x80 ? 1 : String.fromCodePoint(codepoint).length; + codepoint = parseInfo.stream.charCodeAt(i); + i += codepoint < 0x80 ? 1 : String.fromCodePoint(codepoint).length; + continue; + } + if (codepoint !== 0x2d && !isIdentCodepoint(codepoint)) { + return false; + } + } + return true; + } + function isPseudo(parseInfo) { + let position = parseInfo.currentPosition - parseInfo.offset; + let endPosition = parseInfo.currentPosition - parseInfo.offset; + return (parseInfo.stream.charAt(position) == ":" && + parseInfo.stream.charAt(endPosition - 1) == "(" && + (parseInfo.stream.charAt(position + 1) == ":" + ? isIdentToken(parseInfo, 2, -1) + : isIdentToken(parseInfo, 1, -1))) || + parseInfo.stream.charAt(position + 1) == ":" + ? isIdentToken(parseInfo, 2) + : isIdentToken(parseInfo, 1); + } + function startsWith(parseInfo, input) { + let i = 0; + let j = input.length; + while (i < j) { + if (parseInfo.stream.charAt(parseInfo.position - parseInfo.offset + i) != input.charAt(i)) { + return false; + } + i++; + } + return true; + } + function isURLToken(parseInfo) { + let i = parseInfo.position - parseInfo.offset; + let c; + while (++i < parseInfo.currentPosition) { + c = parseInfo.stream.charCodeAt(i); + // single quote or double quote or start parenthesis or close parenthesis + if (isNonPrintable(c) || c == 0x27 || c == 0x22 || c == 0x28 || c == 0x29) { + return false; + } + // valid escape + if (c == 92 /* TokenMap.REVERSE_SOLIDUS */) { + i++; + if (i >= parseInfo.currentPosition) { + return false; + } + c = parseInfo.stream.charCodeAt(i); + // c is not '\n' or '\r' or '\f' + if (c == 0x6e || c == 0x72 || c == 0x66) { + return false; + } + continue; + } + // is white space + if (c == 0x20 || c == 0x09) { + break; + } + } + return i == parseInfo.currentPosition; + } /** * Tokenize CSS string * @param parseInfo @@ -22260,7 +22407,6 @@ function tokenize(parseInfo, yieldEOFToken = true) { if (typeof parseInfo == "string") { parseInfo = { - buffer: "", stream: parseInfo, source: new SourceFile(parseInfo, [], ""), offset: 0, @@ -22269,160 +22415,160 @@ currentPosition: 0, }; } - let value; - let buffer = parseInfo.buffer; let charCode; let nextCharCode; const startTime = performance.now(); const result = []; // allow 10 characters buffer for the streaming parser to avoid incomplete tokens - const endPosition = parseInfo.stream.length - 10; - parseInfo.buffer = ""; - while ((value = peek(parseInfo))) { - charCode = value.charCodeAt(0); + const endPosition = parseInfo.stream.length - 1; + // NaN is not equal to NaN + while ((charCode = peek(parseInfo).charCodeAt(0)) == charCode) { switch (charCode) { case 61 /* TokenMap.EQUALS */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, exports.EnumToken.DelimTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.DelimTokenType)); break; // '+' or '-' case 43 /* TokenMap.PLUS */: case 45 /* TokenMap.MINUS */: - next(parseInfo); - if (charCode === 43 /* TokenMap.PLUS */ && !isNumber(peek(parseInfo))) { - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; - } - result.push(yieldResult(value, parseInfo, SymbolsMapTokens[value])); + nextCharCode = peek(parseInfo).charCodeAt(0); + // not a number + if (charCode === 43 /* TokenMap.PLUS */ && !(nextCharCode >= 0x30 && nextCharCode <= 0x39)) { + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); + } + next(parseInfo); + result.push(yieldResult(parseInfo, SymbolsMapTokens[parseInfo.stream + .slice(parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset) + .toLowerCase()])); break; } - buffer += value; + next(parseInfo); break; // '{' case 123 /* TokenMap.LEFT_BRACE */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, exports.EnumToken.BlockStartTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.BlockStartTokenType)); break; // '}' case 125 /* TokenMap.RIGHT_BRACE */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, exports.EnumToken.BlockEndTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.BlockEndTokenType)); break; // '(' case 40 /* TokenMap.LEFT_PARENTHESIS */: - if (buffer.length > 0) { - if (buffer[0] === ":" && isPseudo(buffer)) { + if (parseInfo.position < parseInfo.currentPosition) { + if (parseInfo.stream[parseInfo.position - parseInfo.offset] === ":" && isPseudo(parseInfo)) { next(parseInfo); - result.push(yieldResult(buffer, parseInfo, exports.EnumToken.PseudoClassFunctionTokenDefType)); - buffer = ""; + result.push(yieldResult(parseInfo, exports.EnumToken.PseudoClassFunctionTokenDefType)); break; } - else if (isIdent(buffer)) { - const hint = buffer.startsWith("--") + else if (isIdentToken(parseInfo)) { + const hint = startsWith(parseInfo, "--") ? exports.EnumToken.CustomFunctionTokenDefType - : (SymbolsMapTokens[buffer.toLowerCase() + "("] ?? exports.EnumToken.FunctionTokenDefType); - result.push(yieldResult(buffer, parseInfo, hint)); + : (SymbolsMapTokens[parseInfo.stream + .slice(parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset) + .toLowerCase() + "("] ?? exports.EnumToken.FunctionTokenDefType); + result.push(yieldResult(parseInfo, hint)); next(parseInfo); - buffer = ""; + // consume '(' + parseInfo.position = parseInfo.currentPosition; if (hint === exports.EnumToken.UrlFunctionTokenDefType) { - buffer = ""; - value = peek(parseInfo); // consume an while (isWhiteSpace(peek(parseInfo).charCodeAt(0))) { - // buffer += next(parseInfo); next(parseInfo); - // charCode = value.charCodeAt(0); } - value = peek(parseInfo); + charCode = peek(parseInfo).charCodeAt(0); let values = null; - if (value == '"' || value == "'") { + if (charCode == 34 /* TokenMap.DOUBLE_QUOTE */ || charCode == 39 /* TokenMap.SINGLE_QUOTE */) { values = consumeString(parseInfo); } else { do { - buffer += next(parseInfo); - value = peek(parseInfo); - charCode = value.charCodeAt(0); + next(parseInfo); + // value = peek(parseInfo); + charCode = peek(parseInfo).charCodeAt(0); } while ( // !(value === "/" && match(parseInfo, "/*") && - value !== ")" && - value !== ""); + charCode !== 41 /* TokenMap.RIGHT_PARENTHESIS */ && + parseInfo.currentPosition < endPosition); } if (values != null) { - if (peek(parseInfo) === "") { + // NaN is not equal to NaN + if ((charCode = peek(parseInfo).charCodeAt(0)) != charCode) { for (let i = 0; i < values.length; i++) { values[i].token.typ = exports.EnumToken.BadUrlTokenType; } } result.push(...values); } - else if (buffer.length > 0) { - result.push(yieldResult(buffer.trimEnd(), parseInfo, - // buffer.length > 0 - peek(parseInfo) === "" || !isURLToken(buffer) + else if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo, + // parseInfo.position < parseInfo.currentPosition + (charCode = peek(parseInfo).charCodeAt(0)) != charCode || !isURLToken(parseInfo) ? exports.EnumToken.BadUrlTokenType : exports.EnumToken.UrlTokenTokenType)); - buffer = ""; } } break; } } - result.push(yieldResult(next(parseInfo), parseInfo, exports.EnumToken.StartParensTokenType)); - buffer = ""; + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.StartParensTokenType)); break; // ')' case 41 /* TokenMap.RIGHT_PARENTHESIS */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, exports.EnumToken.EndParensTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.EndParensTokenType)); break; // '[' case 91 /* TokenMap.LEFT_BRACKETS */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, exports.EnumToken.AttrStartTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.AttrStartTokenType)); break; // ']' case 93 /* TokenMap.RIGHT_BRACKETS */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, exports.EnumToken.AttrEndTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.AttrEndTokenType)); break; case 59 /* TokenMap.SEMICOLON */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + // console.error(parseInfo.stream.length > parseInfo.currentPosition - parseInfo.offset, parseInfo.position < parseInfo.currentPosition, parseInfo.currentPosition - parseInfo.offset, endPosition, parseInfo.stream.slice(parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset), parseInfo.stream.length) + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, exports.EnumToken.SemiColonTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.SemiColonTokenType)); break; case 58 /* TokenMap.COLON */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + // console.error(parseInfo.currentPosition - parseInfo.offset, parseInfo.position - parseInfo.offset, parseInfo.stream.length, parseInfo.stream.slice(parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset)); + result.push(yieldResult(parseInfo)); } next(parseInfo); if (peek(parseInfo).charCodeAt(0) == 58 /* TokenMap.COLON */) { - result.push(yieldResult(value + next(parseInfo), parseInfo, exports.EnumToken.DoubleColonTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.DoubleColonTokenType)); break; } - result.push(yieldResult(value, parseInfo, exports.EnumToken.ColonTokenType)); + result.push(yieldResult(parseInfo, exports.EnumToken.ColonTokenType)); break; // \n \r \f \v \t space case 0x9: @@ -22433,205 +22579,203 @@ case 0xd: case 0x2028: case 0x2029: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - buffer += next(parseInfo); + next(parseInfo); nextCharCode = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset).charCodeAt(0); while (nextCharCode == 0x20 || (nextCharCode >= 0x9 && nextCharCode <= 0xd) || nextCharCode == 0x2028 || nextCharCode == 0x2029) { - value += next(parseInfo); + next(parseInfo); nextCharCode = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset).charCodeAt(0); } - result.push(yieldResult(value, parseInfo, exports.EnumToken.WhitespaceTokenType)); - buffer = ""; + result.push(yieldResult(parseInfo, exports.EnumToken.WhitespaceTokenType)); break; case 44 /* TokenMap.COMMA */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, exports.EnumToken.CommaTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.CommaTokenType)); break; case 36 /* TokenMap.DOLLAR */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } if (match(parseInfo, "$=")) { - result.push(yieldResult(next(parseInfo, 2), parseInfo, exports.EnumToken.EndMatchTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, exports.EnumToken.EndMatchTokenType)); break; } - buffer += next(parseInfo); + next(parseInfo); break; case 126 /* TokenMap.TILDA */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } if (match(parseInfo, "~=")) { - result.push(yieldResult(next(parseInfo, 2), parseInfo, exports.EnumToken.IncludeMatchTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, exports.EnumToken.IncludeMatchTokenType)); break; } - result.push(yieldResult(next(parseInfo), parseInfo, exports.EnumToken.Tilda)); - buffer = ""; + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.Tilda)); break; // case '^': case 94 /* TokenMap.CARET */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } if (match(parseInfo, "^=")) { - result.push(yieldResult(next(parseInfo, 2), parseInfo, exports.EnumToken.StartMatchTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, exports.EnumToken.StartMatchTokenType)); break; } - buffer += next(parseInfo); + next(parseInfo); break; case 42 /* TokenMap.STAR */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } if (match(parseInfo, "*=")) { - result.push(yieldResult(next(parseInfo, 2), parseInfo, exports.EnumToken.ContainMatchTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, exports.EnumToken.ContainMatchTokenType)); break; } - result.push(yieldResult(next(parseInfo), parseInfo, exports.EnumToken.Star)); - buffer = ""; + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.Star)); break; case 38 /* TokenMap.AMPERSAND */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, exports.EnumToken.NestingSelectorTokenType)); - buffer = ""; + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.NestingSelectorTokenType)); break; case 124 /* TokenMap.PIPE */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } // '||' if (match(parseInfo, "||")) { - result.push(yieldResult(next(parseInfo, 2), parseInfo, exports.EnumToken.ColumnCombinatorTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, exports.EnumToken.ColumnCombinatorTokenType)); break; } else if (match(parseInfo, "|=")) { - result.push(yieldResult(next(parseInfo, 2), parseInfo, exports.EnumToken.DashMatchTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, exports.EnumToken.DashMatchTokenType)); break; } - result.push(yieldResult(next(parseInfo), parseInfo, exports.EnumToken.Pipe)); - buffer = ""; + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.Pipe)); break; case 33 /* TokenMap.EXCLAMATION */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } if (match(parseInfo, "!important")) { - result.push(yieldResult(next(parseInfo, 10), parseInfo, exports.EnumToken.ImportantTokenType)); - buffer = ""; + next(parseInfo, 10); + result.push(yieldResult(parseInfo, exports.EnumToken.ImportantTokenType)); break; } - buffer += next(parseInfo); + next(parseInfo); break; case 47 /* TokenMap.SLASH */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } if (!match(parseInfo, "/*")) { - result.push(yieldResult(next(parseInfo), parseInfo, SymbolsMapTokens[value])); + next(parseInfo); + result.push(yieldResult(parseInfo, SymbolsMapTokens[parseInfo.stream.slice(parseInfo.position, parseInfo.currentPosition)])); break; } - buffer += next(parseInfo, 2); - while ((value = next(parseInfo))) { - if (value == "*") { - buffer += value; + next(parseInfo, 2); + while ((charCode = next(parseInfo).charCodeAt(0)) == charCode) { + if (charCode == 42 /* TokenMap.STAR */) { if (match(parseInfo, "/")) { - result.push(yieldResult(buffer + next(parseInfo), parseInfo, exports.EnumToken.CommentTokenType)); - buffer = ""; + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.CommentTokenType)); break; } } - else { - buffer += value; - } + // else { + // buffer += value; + // } } - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo, exports.EnumToken.BadCommentTokenType)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo, exports.EnumToken.BadCommentTokenType)); } break; case 62 /* TokenMap.GREATERTHAN */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } if (match(parseInfo, ">=")) { - result.push(yieldResult(next(parseInfo, 2), parseInfo, exports.EnumToken.GteTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, exports.EnumToken.GteTokenType)); break; } - result.push(yieldResult(next(parseInfo), parseInfo, exports.EnumToken.GtTokenType)); - buffer = ""; + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.GtTokenType)); break; case 60 /* TokenMap.LOWERTHAN */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } if (match(parseInfo, "<=")) { - result.push(yieldResult(next(parseInfo, 2), parseInfo, exports.EnumToken.LteTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, exports.EnumToken.LteTokenType)); break; } - buffer += next(parseInfo); + next(parseInfo); if (match(parseInfo, "!--")) { - buffer += next(parseInfo, 3); - while ((value = next(parseInfo))) { - buffer += value; - if (value == "-" && match(parseInfo, "->")) { + next(parseInfo, 3); + while ((charCode = next(parseInfo).charCodeAt(0)) == charCode) { + if (charCode == 45 /* TokenMap.MINUS */ && match(parseInfo, "->")) { break; } } - if (value === "") { - result.push(yieldResult(buffer, parseInfo, exports.EnumToken.BadCdoTokenType)); + if (parseInfo.currentPosition >= endPosition) { + result.push(yieldResult(parseInfo, exports.EnumToken.BadCdoTokenType)); } else { - result.push(yieldResult(buffer + next(parseInfo, 2), parseInfo, exports.EnumToken.CDOCOMMTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, exports.EnumToken.CDOCOMMTokenType)); } - buffer = ""; } break; case 35 /* TokenMap.HASH */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - buffer += next(parseInfo); + next(parseInfo); break; case 92 /* TokenMap.REVERSE_SOLIDUS */: + if (!yieldEOFToken && parseInfo.stream.length == parseInfo.currentPosition - parseInfo.offset + 1) { + break; + } + // console.error('reverse solidus', parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset, parseInfo.stream.length, parseInfo.stream.slice(parseInfo.position, parseInfo.currentPosition)); next(parseInfo); // EOF if (!peek(parseInfo)) { + if (!yieldEOFToken) { + break; + } // end of stream ignore \\ - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } break; } - buffer += value + next(parseInfo); + next(parseInfo); break; case 39 /* TokenMap.SINGLE_QUOTE */: case 34 /* TokenMap.DOUBLE_QUOTE */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } result.push(...consumeString(parseInfo)); break; @@ -22639,30 +22783,31 @@ const codepoint = parseInfo.stream .charAt(parseInfo.currentPosition - parseInfo.offset + 1) .charCodeAt(0); - if (!isDigit(codepoint) && buffer !== "") { - result.push(yieldResult(buffer, parseInfo)); - buffer = next(parseInfo, 2); + if (!isDigit(codepoint) && parseInfo.position !== parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); + next(parseInfo, 2); break; } - buffer += next(parseInfo); + next(parseInfo); break; default: - buffer += next(parseInfo); + next(parseInfo); break; } - if (!yieldEOFToken && endPosition <= parseInfo.stream.length - parseInfo.currentPosition + parseInfo.offset) { + if (!yieldEOFToken && endPosition <= parseInfo.currentPosition - parseInfo.offset + 1) { break; } } if (yieldEOFToken) { - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); + // console.error(parseInfo.stream.length > parseInfo.currentPosition - parseInfo.offset, parseInfo.position < parseInfo.currentPosition, parseInfo.currentPosition - parseInfo.offset, parseInfo.position - parseInfo.offset, parseInfo.stream.length); + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult("", parseInfo, exports.EnumToken.EOFTokenType)); - } - else { - parseInfo.buffer = buffer; + result.push(yieldResult(parseInfo, exports.EnumToken.EOFTokenType)); } + // else { + // // parseInfo.buffer = buffer; + // } parseInfo.time += performance.now() - startTime; return result; } @@ -22674,19 +22819,17 @@ async function* tokenizeStream(input, parseInfo) { const decoder = new TextDecoder("utf-8"); const reader = input.getReader(); + parseInfo.stream = ""; while (true) { const { done, value } = await reader.read(); const stream = ArrayBuffer.isView(value) ? decoder.decode(value, { stream: true }) : value; if (!done) { parseInfo.source.append(stream); - if (typeof parseInfo.stream != "string") { - parseInfo.stream = stream; - } - else { - parseInfo.stream = (parseInfo.stream.slice(parseInfo.currentPosition - parseInfo.offset) + - stream); - } - parseInfo.offset = parseInfo.currentPosition; + parseInfo.stream = (parseInfo.stream.slice(parseInfo.position - parseInfo.offset) + stream); + parseInfo.offset = parseInfo.offset = parseInfo.position; + } + else { + parseInfo.stream = ""; } yield* tokenize(parseInfo, done); if (done) { @@ -22758,9 +22901,12 @@ if (rules.includes(replacement.typ) && !Array.isArray(replacement[TOKENS])) { replacement[TOKENS] = parseString(replacement.typ == exports.EnumToken.RuleNodeType || replacement.typ === exports.EnumToken.KeyframesRuleNodeType ? replacement.sel - : replacement.nam); + : // @ts-ignore + replacement.nam); } - const result = feature.run(replacement, options2, parent[PARENT] ?? ast, context, exports.FeatureWalkMode.Pre); + const result = feature.run(replacement, options2, + // @ts-ignore + parent[PARENT] ?? ast, context, exports.FeatureWalkMode.Pre); if (result != null) { replacement = result; } @@ -22769,9 +22915,12 @@ (!Array.isArray(replacement) || replacement.length > 0) && replacement != parent && parent[PARENT] != null) { + // @ts-ignore replaceNodeOrValue(parent[PARENT], parent, replacement); } + // @ts-ignore if (replacement.chi != null) { + // @ts-ignore for (const node of replacement.chi) { node[PARENT] = replacement; parents.add(node); @@ -22798,7 +22947,9 @@ (feature.accept != null && !feature.accept.has(parent.typ))) { continue; } - const result = feature.run(replacement, options2, parent[PARENT] ?? ast, context, exports.FeatureWalkMode.Post); + const result = feature.run(replacement, options2, + // @ts-ignore + parent[PARENT] ?? ast, context, exports.FeatureWalkMode.Post); if (result != null) { replacement = result; } @@ -22811,7 +22962,9 @@ // @ts-ignore replaceNodeOrValue(parent[PARENT], parent, replacement); } + // @ts-ignore if (replacement.chi != null) { + // @ts-ignore for (const node of replacement.chi) { node[PARENT] = replacement; parents.add(node); @@ -22842,6 +22995,7 @@ values[values.indexOf(value)] = value.l; } else { + // @ts-ignore replaceNodeOrValue(parent, value, value.l); // @ts-ignore value = value.l; @@ -22898,9 +23052,11 @@ // @ts-expect-error const p = parents?.[parents?.indexOf?.(parent) + 1]; if (p != null) { + // @ts-ignore replaceNodeOrValue(p, parent, replacement); } else { + // @ts-ignore values.splice(values.indexOf(parent), 1, replacement); } hasUpdates = true; @@ -23014,6 +23170,7 @@ continue; } while (previous?.typ === exports.EnumToken.CommentNodeType) { + // @ts-ignore previous = ast.chi[--nodeIndex]; } node = ast.chi[i]; @@ -23036,6 +23193,7 @@ // do not merge keyframes // https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@keyframes#resolving_duplicates previous.chi.push(...node.chi); + // @ts-ignore ast.chi.splice(i, 1); previous = ast?.chi?.[nodeIndex] ?? null; i = nodeIndex; @@ -23108,6 +23266,7 @@ else if (ast.typ === node.typ && ast.nam === node.nam && ast.val === node.val) { + // @ts-ignore replaceNodeOrValue(ast, node, node.chi); i--; continue; @@ -23201,7 +23360,9 @@ ":is(" + node[OPTIMIZED].selector.reduce(reducer, []).join(",") + ")"; - const sel2 = node[OPTIMIZED].selector.reduce((acc, curr) => (acc.length > 0 ? acc + "," : "") + node[OPTIMIZED].optimized[0] + curr.join(""), ""); + const sel2 = node[OPTIMIZED].selector.reduce((acc, curr) => + // @ts-ignore + (acc.length > 0 ? acc + "," : "") + node[OPTIMIZED].optimized[0] + curr.join(""), ""); node.sel = sel1.length < sel2.length ? sel1 : sel2; node[TOKENS] = null; } @@ -23281,7 +23442,9 @@ ":is(" + node[OPTIMIZED].selector.reduce(reducer, []).join(",") + ")"; - const sel2 = node[OPTIMIZED].selector.reduce((acc, curr) => (acc.length > 0 ? acc + "," : "") + node[OPTIMIZED].optimized[0] + curr.join(""), ""); + const sel2 = node[OPTIMIZED].selector.reduce((acc, curr) => + // @ts-ignore + (acc.length > 0 ? acc + "," : "") + node[OPTIMIZED].optimized[0] + curr.join(""), ""); node.sel = sel1.length < sel2.length ? sel1 : sel2; node[TOKENS] = null; } @@ -23292,11 +23455,14 @@ curr.join(""), ""); node[TOKENS] = null; } + // @ts-ignore } else if (node[OPTIMIZED]?.optimized.length > 0) { + // @ts-ignore const sel = node[OPTIMIZED].optimized.join(""); if (sel.length < node.sel.length) { node.sel = sel; + // @ts-ignore node[RAW] = [node[OPTIMIZED].optimized.slice()]; node[TOKENS] = null; } @@ -23321,9 +23487,12 @@ if (((node.typ === exports.EnumToken.RuleNodeType || node.typ === exports.EnumToken.KeyframesRuleNodeType) && node.sel === previous.sel) || + // @ts-ignore (node.typ == exports.EnumToken.AtRuleNodeType && node.nam !== "font-face" && + // @ts-ignore node.nam === previous.nam)) { + // @ts-ignore node.chi.unshift(...previous.chi); doMinify(node, options, recursive, errors, nestingContent, context); ast.chi.splice(nodeIndex, 1); @@ -26158,6 +26327,29 @@ func.val == ":nth-last-child" || func.val == ":nth-of-type" || func.val == ":nth-last-of-type") { + const list = []; + let index; + for (index = 0; index < func.chi.length; index++) { + if (func.chi[index].typ == exports.EnumToken.CommentTokenType || func.chi[index].typ == exports.EnumToken.WhitespaceTokenType) { + continue; + } + if (func.chi[index].typ == exports.EnumToken.IdenTokenType && equalsIgnoreCase('of', func.chi[index].val)) { + index--; + break; + } + list.push(func.chi[index]); + } + if (list.length == 3) { + if (list[0].typ == exports.EnumToken.IdenTokenType && ('n' == list[0].val || '-n' == list[0].val || '+n' == list[0].val)) { + if (list[1].typ == exports.EnumToken.NextSiblingCombinatorTokenType) { + if (list[2].typ == exports.EnumToken.NumberTokenType && (0 == list[2].val)) { + list[0].val = 'n'; + func.chi.splice(0, index, list[0]); + break; + } + } + } + } const token = func.chi.find((t) => t.typ != exports.EnumToken.WhitespaceTokenType && t.typ != exports.EnumToken.CommentTokenType); if (token?.typ == exports.EnumToken.IdenTokenType || token?.typ == exports.EnumToken.LiteralTokenType) { if (token.typ == exports.EnumToken.IdenTokenType && @@ -27973,7 +28165,8 @@ success = false; errors.push({ action: "drop", - node: options.source.getSourceLocation(stream[i][LOC].sta), + node: stream[i], + location: options.source.getSourceLocation(stream[i][LOC].sta), message: ` is not allowed outside of parentheses`, }); break; @@ -29032,7 +29225,9 @@ break; } } + // @ts-ignore if (nodes[i].chi != null) { + // @ts-ignore subNodes.push(...nodes[i].chi); } if (subNodes.length > 0) { @@ -29151,6 +29346,7 @@ } } if (node != nodes[i]) { + // @ts-ignore replaceNodeOrValue(nodes[i][PARENT], nodes[i], node); } } @@ -29270,6 +29466,7 @@ throw new Error("css variable import not supported by parseSync() or transformSync(). use parse() or transform() instead.\nat " + options.source.getSourceLocation(node[LOC].sta).join(":")); } + // @ts-ignore if (node.typ == exports.EnumToken.CssVariableDeclarationMapTokenType) { const from = node.from.find((t) => t.typ == exports.EnumToken.IdenTokenType || isIdentColor(t)); if (!(from.val in cssVariablesMap)) { @@ -29544,6 +29741,7 @@ } else if ((value.typ == exports.EnumToken.IdenTokenType || isIdentColor(value)) && value.val in importedCssVariables) { + // @ts-ignore replaceNodeOrValue(parent, value, importedCssVariables[value.val].val); } } @@ -29619,7 +29817,7 @@ } if (moduleSettings.scoped & exports.ModuleScopeEnumOptions.Pure) { if (!hasIdOrClass) { - throw new Error(`pure module: No id or class found in selector '${node.sel}' at '${node[LOC]?.src ?? ""}':${node[LOC]?.sta?.lin ?? ""}:${node[LOC]?.sta?.col ?? ""}`); + throw new Error(`pure module: No id or class found in selector '${node.sel}' at '${options.source.getOffsets(node[LOC]?.sta).join(":")}'`); } } node.sel = ""; @@ -29905,6 +30103,7 @@ stats.nodesCount += root.stats.nodesCount; stats.tokensCount += root.stats.tokensCount; stats.imports.push(root.stats); + // @ts-ignore node[PARENT].chi.splice(node[PARENT].chi.indexOf(node), 1, ...root.ast.chi); if (root.errors.length > 0) { errors.push(...root.errors); @@ -29951,7 +30150,9 @@ break; } } + // @ts-ignore if (nodes[i].chi != null) { + // @ts-ignore subNodes.push(...nodes[i].chi); } if (subNodes.length > 0) { @@ -30073,6 +30274,7 @@ } } if (node != nodes[i]) { + // @ts-ignore replaceNodeOrValue(nodes[i][PARENT], nodes[i], node); } } @@ -30217,6 +30419,7 @@ parent.chi.splice(parent.chi.indexOf(node), 1); continue; } + // @ts-ignore if (node.typ == exports.EnumToken.CssVariableDeclarationMapTokenType) { const from = node.from.find((t) => t.typ == exports.EnumToken.IdenTokenType || isIdentColor(t)); if (!(from.val in cssVariablesMap)) { @@ -30261,6 +30464,7 @@ let result = moduleSettings.scoped & exports.ModuleScopeEnumOptions.Global ? node.nam : moduleSettings.generateScopedName(node.nam, moduleSettings.filePath, moduleSettings.pattern, moduleSettings.hashLength); + // @ts-ignore let value = result instanceof Promise ? await result : result; mapping[node.nam] = "--" + @@ -30568,30 +30772,13 @@ } for (const { value, parent } of walkValues(node.val, node)) { if (value.typ == exports.EnumToken.DashedIdenTokenType) { - // if (!((value as DashedIdentToken).val in mapping)) { - // const result = - // moduleSettings.scoped! & ModuleScopeEnumOptions.Global - // ? (value as DashedIdentToken).val - // : moduleSettings.generateScopedName!( - // (value as DashedIdentToken).val, - // moduleSettings.filePath as string, - // moduleSettings.pattern as string, - // moduleSettings.hashLength, - // ); - // let val: string = result instanceof Promise ? await result : result; - // mapping[(value as DashedIdentToken).val] = - // "--" + - // (moduleSettings.naming! & ModuleCaseTransformEnum.DashCaseOnly || - // moduleSettings.naming! & ModuleCaseTransformEnum.CamelCaseOnly - // ? getKeyName(val, moduleSettings.naming as ModuleCaseTransformEnum) - // : val); - // revMapping[mapping[(value as DashedIdentToken).val]] = (value as DashedIdentToken).val; - // } value.val = mapping[value.val]; } else if ((value.typ == exports.EnumToken.IdenTokenType || isIdentColor(value)) && value.val in importedCssVariables) { - replaceNodeOrValue(parent, value, importedCssVariables[value.val].val); + replaceNodeOrValue( + // @ts-ignore + parent, value, importedCssVariables[value.val].val); } } } @@ -30669,7 +30856,7 @@ } if (moduleSettings.scoped & exports.ModuleScopeEnumOptions.Pure) { if (!hasIdOrClass) { - throw new Error(`pure module: No id or class found in selector '${node.sel}' at '${node[LOC]?.src ?? ""}':${node[LOC]?.sta?.lin ?? ""}:${node[LOC]?.sta?.col ?? ""}`); + throw new Error(`pure module: No id or class found in selector '${node.sel}' at '${(options.source?.getOffsets?.(node[LOC]?.sta) ?? []).join(":")}'`); } } node.sel = ""; @@ -31289,10 +31476,13 @@ let definedAfterLastElse = false; if (sibling == null || sibling.typ !== exports.EnumToken.AtRuleNodeType) { missingWhen = true; + // @ts-expect-error } else if (sibling.nam !== "when") { + // @ts-expect-error if (sibling.nam !== "else") { missingWhen = true; + // @ts-expect-error } else if (sibling.val === "") { definedAfterLastElse = true; @@ -31987,6 +32177,12 @@ } return result; } + /** + * + * @param options + * @param prefix + * @private + */ function validateSyncArguments(options, prefix = "options.") { const args = Object.entries(options); let i; @@ -32003,6 +32199,52 @@ } } + /** + * set node property + * @param node + * @param property + * @param value + */ + function setNodeProperty(node, property, value) { + switch (property) { + case "location": + node[LOC] = value; + break; + case "state": + node[STATE] = value; + break; + case "errors": + node[ERRORS] = value; + break; + case "tokens": + node[TOKENS] = value; + break; + case "parent": + node[PARENT] = value; + break; + } + } + /** + * get node property + * @param node + * @param property + * @returns + */ + function getNodeProperty(node, property) { + switch (property) { + case "location": + return node[LOC]; + case "state": + return node[STATE]; + case "errors": + return node[ERRORS]; + case "tokens": + return node[TOKENS]; + case "parent": + return node[PARENT]; + } + } + /** * Load file or url * @param url @@ -32082,7 +32324,7 @@ }), mapping); } /** - * Parse css file + * Parse CSS file * @param file url or path * @param options * @param asStream load file as stream @@ -32110,9 +32352,8 @@ return parse({ file, asStream, ...options }); } /** - * Parse css + * Parse CSS * @param args - * @private * * Parsing a string * @@ -32183,7 +32424,6 @@ * ``` * * @param args - * @private */ function transformSync(...args) { let options; @@ -32232,7 +32472,7 @@ }; } /** - * Parse css + * Parse CSS * * Example: * @@ -32257,7 +32497,6 @@ * console.log(result.ast); * ``` * @param args - * @private */ async function parse(...args) { let options; @@ -32336,7 +32575,7 @@ }); } /** - * Transform css + * Transform CSS * * Example: * @@ -32355,7 +32594,6 @@ * console.log(result.code); * ``` * @param args - * @private */ async function transform(...args) { let options; @@ -32418,6 +32656,7 @@ exports.findAll = findAll; exports.findByValue = findByValue; exports.findLast = findLast; + exports.getNodeProperty = getNodeProperty; exports.isOkLabClose = isOkLabClose; exports.load = load; exports.minify = minify; @@ -32431,6 +32670,7 @@ exports.renderToken = renderValue; exports.replaceNodeOrValue = replaceNodeOrValue; exports.resolve = resolve; + exports.setNodeProperty = setNodeProperty; exports.transform = transform; exports.transformFile = transformFile; exports.transformSync = transformSync; diff --git a/dist/index.cjs b/dist/index.cjs index 0f73af61..d7f70875 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -9553,13 +9553,18 @@ function* walk(node, filter, reverse) { } if (includeValues) { if (node[TOKENS] != null) { + // @ts-ignore parents.splice(i, 0, ...(reverse ? node[TOKENS].toReversed() : node[TOKENS])); + // @ts-ignore } else if (Array.isArray(node.val)) { + // @ts-ignore parents.splice(i, 0, ...(reverse ? node.val.toReversed() : node.val)); } } + // @ts-ignore if (node["chi"] != null && (!isNumeric || (option & exports.WalkerOptionEnum.IgnoreChildren) === 0)) { + // @ts-ignore parents.splice(i, 0, ...(reverse ? node.chi.toReversed() : node.chi)); for (const child of node.chi) { map.set(child, node); @@ -10939,7 +10944,7 @@ function getTokenType(token, position, currentPosition) { [LOC]: pos, }; } - if (isPseudo(token)) { + if (isPseudo$1(token)) { return { typ: ValidationTokenEnum.PseudoClassToken, val: token, @@ -11696,23 +11701,28 @@ function cloneNode(node, cloneChildren = false, cloneMap = null) { cloneMap?.set?.(node, clone); for (const [name, value] of Object.entries(node)) { if (value == null || typeof value != "object") { + // @ts-ignore clone[name] = value; } else if (Array.isArray(value)) { + // @ts-ignore clone[name] = []; if (cloneChildren || name !== checkNode) { for (const c of value) { const newObj = cloneNode(c, cloneChildren, cloneMap); cloneMap?.set?.(c, newObj); + // @ts-ignore clone[name].push(newObj); } } } else { + // @ts-ignore clone[name] = { ...value }; } } for (const symbol of Object.getOwnPropertySymbols(node)) { + // @ts-ignore clone[symbol] = node[symbol]; } return clone; @@ -12701,6 +12711,7 @@ function matchSyntax(syntaxes, context, options) { return result; } if (tokensfuncDefMap.has(token.typ) && + // @ts-ignore token.typ === exports.EnumToken.WildCardFunctionTokenDefType) { const range = trimArray(context.peekRange()); result = matchSyntax(getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, token.val + "()")?.[0]?.chi, createValidationContext(range.slice(1, -1)), options); @@ -13701,7 +13712,9 @@ function matchProperty(property, context, options) { // ) // ) { const newRange = range.map((t) => cloneNode(t, true)); + // @ts-ignore parseTokens(newRange, { parseColor: true }, errors); + // @ts-ignore success = newRange.length == 1 && isColor(newRange[0], errors); if (success) { context.update(range.at(-1)); @@ -15812,6 +15825,9 @@ const isIdent = memoize(function (name) { return false; } if (codepoint == REVERSE_SOLIDUS) { + if (i + 1 > j) { + return false; + } codepoint = name.charCodeAt(i + 1); // if (!isIdentCodepoint(codepoint)) { // return false; @@ -15848,36 +15864,7 @@ function isNonPrintable(codepoint) { codepoint == 0x7f || (codepoint >= 0xe && codepoint <= 0x1f)); } -function isURLToken(str) { - let i = -1; - let c; - while (++i < str.length) { - c = str.charCodeAt(i); - // single quote or double quote or start parenthesis or close parenthesis - if (isNonPrintable(c) || c == 0x27 || c == 0x22 || c == 0x28 || c == 0x29) { - return false; - } - // valid escape - if (c == REVERSE_SOLIDUS) { - i++; - if (i >= str.length) { - return false; - } - c = str.charCodeAt(i); - // c is not '\n' or '\r' or '\f' - if (c == 0x6e || c == 0x72 || c == 0x66) { - return false; - } - continue; - } - // is white space - if (c == 0x20 || c == 0x09) { - break; - } - } - return i == str.length; -} -function isPseudo(name) { +function isPseudo$1(name) { return (name.charAt(0) == ":" && ((name.endsWith("(") && isIdent(name.charAt(1) == ":" ? name.slice(2, -1) : name.slice(1, -1))) || isIdent(name.charAt(1) == ":" ? name.slice(2) : name.slice(1)))); @@ -18884,7 +18871,9 @@ class PropertyMap { if (t.typ == exports.EnumToken.ImportantTokenType) { isImportant = true; } - if (filtered.length == 0 && t.typ != exports.EnumToken.WhitespaceTokenType && t.typ != exports.EnumToken.ImportantTokenType) { + if (filtered.length == 0 && + t.typ != exports.EnumToken.WhitespaceTokenType && + t.typ != exports.EnumToken.ImportantTokenType) { filtered.push(dec); } } @@ -19763,7 +19752,8 @@ class ComputeCalcExpressionFeature { // @ts-ignore const children = parent.typ == exports.EnumToken.DeclarationNodeType ? parent.val - : parent.chi; + : // @ts-ignore + parent.chi; if (values.length == 1 && values[0].typ != exports.EnumToken.BinaryExpressionTokenType) { for (let i = 0; i < children.length; i++) { if (children[i] == value) { @@ -21102,6 +21092,7 @@ function findByValue(ast, matcher) { } for (const { value, parent, root: rootNode, parents } of walkValues(source, node)) { if (matcher(value, node)) { + // @ts-ignore return { node, value: { node: value, parent, root: rootNode, parents } }; } } @@ -21210,6 +21201,7 @@ function substituteIfElseNode(declaration, node, wrapper, parentWrapper, cache) exports.EnumToken.SemiColonTokenType ? trimArray(node.r.r.slice(0, -1)) : node.r.r); + // @ts-expect-error if (targetParentWrapper.typ != exports.EnumToken.DeclarationNodeType) { let index = targetParentWrapper.chi.indexOf(targetWrapper); if (index != -1) { @@ -21231,6 +21223,7 @@ function substituteIfElseNode(declaration, node, wrapper, parentWrapper, cache) .r.r.slice(0, -1)) : siblingWrapper.chi[k] .r.r); + // @ts-ignore cache.add(siblingWrapper.chi[k].l); } } @@ -21254,7 +21247,9 @@ function substituteIfElseNode(declaration, node, wrapper, parentWrapper, cache) } if (left.typ === exports.EnumToken.IdenTokenType && equalsIgnoreCase("else", left.val)) { clonedDeclaration = cloneNode(declaration, true, nodeMap); - replaceNodeOrValue(nodeMap.get(parentWrapper), nodeMap.get(targetWrapper.typ === exports.EnumToken.DeclarationNodeType ? node : targetWrapper), node.r.at(-1)?.typ === exports.EnumToken.SemiColonTokenType ? trimArray(node.r.slice(0, -1)) : node.r); + replaceNodeOrValue(nodeMap.get(parentWrapper), + // @ts-expect-error + nodeMap.get(targetWrapper.typ === exports.EnumToken.DeclarationNodeType ? node : targetWrapper), node.r.at(-1)?.typ === exports.EnumToken.SemiColonTokenType ? trimArray(node.r.slice(0, -1)) : node.r); result.push(clonedDeclaration); } else if (left?.typ === exports.EnumToken.WhenElseFunctionTokenType) { @@ -21321,17 +21316,27 @@ function processNode(declarationNode, cache) { const parentWrapper = node.parent ?? parents.find((node) => !nodeMatcher(node)); if (node.node.typ === exports.EnumToken.WildCardFunctionTokenType) { for (i = 0; i < node.node.chi.length; i++) { - stack.push(...substituteIfElseNode(declaration, node.node.chi[i], node.node, parentWrapper, cache)); + stack.push( + // @ts-expect-error + ...substituteIfElseNode( + // @ts-expect-error + declaration, node.node.chi[i], node.node, parentWrapper, cache)); } } else { - stack.push(...substituteIfElseNode(declaration, node.node, parentWrapper, parents[parents.indexOf(parentWrapper) + 1] ?? declaration, cache)); + stack.push( + // @ts-expect-error + ...substituteIfElseNode( + // @ts-expect-error + declaration, node.node, parentWrapper, parents[parents.indexOf(parentWrapper) + 1] ?? declaration, cache)); } } if (result.length > 0) { + // @ts-expect-error replaceNodeOrValue(declarationNode[PARENT], declarationNode, result); } // else remove node? + // @ts-expect-error return result; } class ExpandIfFeature { @@ -21493,7 +21498,6 @@ class SourceMap { /** * * @param sourcemaps - * @private */ constructor(sourcemaps) { if (typeof sourcemaps === "string") { @@ -21551,7 +21555,6 @@ class SourceMap { * Add all location * @param maps * @throws - * @private */ add(...maps) { let srcIndex; @@ -21599,6 +21602,7 @@ class SourceMap { let sourceFileIndex = 0; // second field let sourceCodeLine = 0; // third field let sourceCodeColumn = 0; // fourth field + // let nameIndex: number = 0; // fifth field let generatedCodeColumn; let result; // mappings to original source @@ -22014,17 +22018,17 @@ var TokenMap; TokenMap[TokenMap["GREATERTHAN"] = 62] = "GREATERTHAN"; })(TokenMap || (TokenMap = {})); function consumeString(parseInfo) { - const quote = next(parseInfo); - let value; - let buffer = quote; + const quote = next(parseInfo).charCodeAt(0); + let charCode; + let decodeSegments = false; const result = []; - while ((value = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset))) { - if (value == "\\") { - if ("\\" == parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset + 1)) { - buffer += next(parseInfo, 2); + while ((charCode = parseInfo.stream.charCodeAt(parseInfo.currentPosition - parseInfo.offset)) == charCode) { + if (charCode == 92 /* TokenMap.REVERSE_SOLIDUS */) { + if (charCode == parseInfo.stream.charCodeAt(parseInfo.currentPosition - parseInfo.offset + 1)) { + next(parseInfo, 2); continue; } - const sequence = peek(parseInfo, 6); + const sequence = peek(parseInfo, 7); let escapeSequence = ""; let codepoint; let i; @@ -22043,50 +22047,72 @@ function consumeString(parseInfo) { break; } if (escapeSequence.trimEnd().length > 0) { - const codepoint = parseInt(escapeSequence, 16); - if (codepoint == 0 || - // leading surrogate - (0xd800 <= codepoint && codepoint <= 0xdbff) || - // trailing surrogate - (0xdc00 <= codepoint && codepoint <= 0xdfff)) { - buffer += String.fromCodePoint(0xfffd); - } - else { - buffer += String.fromCodePoint(codepoint); - } - next(parseInfo, escapeSequence.length + + // const codepoint = parseInt(escapeSequence, 16); + // TODO set decode flag ON + // if ( + // codepoint == 0 || + // // leading surrogate + // (0xd800 <= codepoint && codepoint <= 0xdbff) || + // // trailing surrogate + // (0xdc00 <= codepoint && codepoint <= 0xdfff) + // ) { + // buffer += String.fromCodePoint(0xfffd); + // } else { + // buffer += String.fromCodePoint(codepoint); + // } + const length = escapeSequence.length + 1 + (isWhiteSpace(parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset)?.charCodeAt(0)) ? 1 - : 0)); + : 0); + decodeSegments = true; + next(parseInfo, length); continue; } - buffer += next(parseInfo, 2); + next(parseInfo, 2); continue; } - if (value == quote) { - buffer += value; - result.push(yieldResult(buffer, parseInfo, - /* hasNewLine ? EnumToken.BadStringTokenType : */ exports.EnumToken.StringTokenType)); + if (charCode == quote) { next(parseInfo); - buffer = ""; + result.push(yieldResult(parseInfo, + /* hasNewLine ? EnumToken.BadStringTokenType : */ exports.EnumToken.StringTokenType, decodeSegments ? { decodeSegments } : null)); return result; } - if (isNewLine(value.charCodeAt(0))) { - result.push(yieldResult(buffer + next(parseInfo), parseInfo, exports.EnumToken.BadStringTokenType)); + if (isNewLine(charCode)) { + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.BadStringTokenType)); return result; } - buffer += value; next(parseInfo); } // EOF - 'Unclosed-string' fixed - result.push(yieldResult(buffer + quote, parseInfo, exports.EnumToken.StringTokenType)); + result.push(yieldResult(parseInfo, exports.EnumToken.StringTokenType)); return result; } -function yieldResult(val, parseInfo, hint) { +function yieldResult(parseInfo, hint, options) { + let val = parseInfo.stream.slice(parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset); let token = null; let dimension; - // console.debug(`Yield result: ${val}, ${hint}`); + // if (val === "" && hint != EnumToken.EOFTokenType) { + // console.error(parseInfo.stream.length > parseInfo.currentPosition - parseInfo.offset, parseInfo.position < parseInfo.currentPosition, parseInfo.currentPosition - parseInfo.offset, parseInfo.stream.slice(parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset), parseInfo.stream.length, parseInfo.currentPosition- parseInfo.offset, parseInfo.position- parseInfo.offset, parseInfo.position, parseInfo.currentPosition, parseInfo.offset) + // console.error(new Error(`val is empty '${hint}'`)); + // } + // console.error({val, hint, position: parseInfo.position - parseInfo.offset, currentPosition: parseInfo.currentPosition - parseInfo.offset, endPosition: parseInfo.stream.length, offset: parseInfo.offset, len: parseInfo.stream.length}); + // console.error(new Error('incomplete token')); + if (options?.decodeSegments) { + val = val.replace(/\\([0-9a-fA-F]{1,6})(?:\s)?/g, (_, sequence) => { + const codepoint = parseInt(sequence, 16); + if (codepoint == 0 || + // leading surrogate + (0xd800 <= codepoint && codepoint <= 0xdbff) || + // trailing surrogate + (0xdc00 <= codepoint && codepoint <= 0xdfff) || + codepoint > 0x10ffff) { + return "\uFFFD"; + } + return String.fromCodePoint(codepoint); + }); + } if (hint != null) { let searchArray = null; switch (hint) { @@ -22255,6 +22281,127 @@ function next(parseInfo, count = 1) { parseInfo.currentPosition += char.length; return char; } +function isIdentToken(parseInfo, start, end) { + let j = parseInfo.currentPosition - parseInfo.offset; + let i = parseInfo.position - parseInfo.offset; + if (start != null) { + if (end == null) { + if (start < 0) { + j += start; + } + else { + i += start; + } + } + else { + if (end < 0) { + j += end; + } + else { + j = parseInfo.position + end; + } + } + } + j--; + let codepoint = parseInfo.stream.charCodeAt(i); + // - + if (codepoint == 0x2d) { + let nextCodepoint; + if ((nextCodepoint = parseInfo.stream.charCodeAt(i + 1)) != nextCodepoint) { + return false; + } + // if (nextCodepoint == REVERSE_SOLIDUS) { + // return name.length > 2 && !isNewLine(name.charCodeAt(2) as number); + // } + if (isDigit(nextCodepoint)) { + return false; + } + codepoint = nextCodepoint; + i++; + } + if (codepoint !== 0x2d && !isIdentStart(codepoint)) { + return false; + } + if (codepoint == 92 /* TokenMap.REVERSE_SOLIDUS */) { + codepoint = parseInfo.stream.charCodeAt(i + 1); + // if (!isIdentCodepoint(codepoint)) { + // return false; + // } + i += String.fromCodePoint(codepoint).length; + // if (i < j) { + // codepoint = name.charCodeAt(i) as number; + // if (!isIdentCodepoint(codepoint)) { + // return false; + // } + // } + } + while (i < j) { + i += codepoint < 0x80 ? 1 : String.fromCodePoint(codepoint).length; + codepoint = parseInfo.stream.charCodeAt(i); + if (codepoint == 92 /* TokenMap.REVERSE_SOLIDUS */) { + i += codepoint < 0x80 ? 1 : String.fromCodePoint(codepoint).length; + codepoint = parseInfo.stream.charCodeAt(i); + i += codepoint < 0x80 ? 1 : String.fromCodePoint(codepoint).length; + continue; + } + if (codepoint !== 0x2d && !isIdentCodepoint(codepoint)) { + return false; + } + } + return true; +} +function isPseudo(parseInfo) { + let position = parseInfo.currentPosition - parseInfo.offset; + let endPosition = parseInfo.currentPosition - parseInfo.offset; + return (parseInfo.stream.charAt(position) == ":" && + parseInfo.stream.charAt(endPosition - 1) == "(" && + (parseInfo.stream.charAt(position + 1) == ":" + ? isIdentToken(parseInfo, 2, -1) + : isIdentToken(parseInfo, 1, -1))) || + parseInfo.stream.charAt(position + 1) == ":" + ? isIdentToken(parseInfo, 2) + : isIdentToken(parseInfo, 1); +} +function startsWith(parseInfo, input) { + let i = 0; + let j = input.length; + while (i < j) { + if (parseInfo.stream.charAt(parseInfo.position - parseInfo.offset + i) != input.charAt(i)) { + return false; + } + i++; + } + return true; +} +function isURLToken(parseInfo) { + let i = parseInfo.position - parseInfo.offset; + let c; + while (++i < parseInfo.currentPosition) { + c = parseInfo.stream.charCodeAt(i); + // single quote or double quote or start parenthesis or close parenthesis + if (isNonPrintable(c) || c == 0x27 || c == 0x22 || c == 0x28 || c == 0x29) { + return false; + } + // valid escape + if (c == 92 /* TokenMap.REVERSE_SOLIDUS */) { + i++; + if (i >= parseInfo.currentPosition) { + return false; + } + c = parseInfo.stream.charCodeAt(i); + // c is not '\n' or '\r' or '\f' + if (c == 0x6e || c == 0x72 || c == 0x66) { + return false; + } + continue; + } + // is white space + if (c == 0x20 || c == 0x09) { + break; + } + } + return i == parseInfo.currentPosition; +} /** * Tokenize CSS string * @param parseInfo @@ -22263,7 +22410,6 @@ function next(parseInfo, count = 1) { function tokenize(parseInfo, yieldEOFToken = true) { if (typeof parseInfo == "string") { parseInfo = { - buffer: "", stream: parseInfo, source: new SourceFile(parseInfo, [], ""), offset: 0, @@ -22272,160 +22418,160 @@ function tokenize(parseInfo, yieldEOFToken = true) { currentPosition: 0, }; } - let value; - let buffer = parseInfo.buffer; let charCode; let nextCharCode; const startTime = performance.now(); const result = []; // allow 10 characters buffer for the streaming parser to avoid incomplete tokens - const endPosition = parseInfo.stream.length - 10; - parseInfo.buffer = ""; - while ((value = peek(parseInfo))) { - charCode = value.charCodeAt(0); + const endPosition = parseInfo.stream.length - 1; + // NaN is not equal to NaN + while ((charCode = peek(parseInfo).charCodeAt(0)) == charCode) { switch (charCode) { case 61 /* TokenMap.EQUALS */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, exports.EnumToken.DelimTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.DelimTokenType)); break; // '+' or '-' case 43 /* TokenMap.PLUS */: case 45 /* TokenMap.MINUS */: - next(parseInfo); - if (charCode === 43 /* TokenMap.PLUS */ && !isNumber(peek(parseInfo))) { - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; - } - result.push(yieldResult(value, parseInfo, SymbolsMapTokens[value])); + nextCharCode = peek(parseInfo).charCodeAt(0); + // not a number + if (charCode === 43 /* TokenMap.PLUS */ && !(nextCharCode >= 0x30 && nextCharCode <= 0x39)) { + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); + } + next(parseInfo); + result.push(yieldResult(parseInfo, SymbolsMapTokens[parseInfo.stream + .slice(parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset) + .toLowerCase()])); break; } - buffer += value; + next(parseInfo); break; // '{' case 123 /* TokenMap.LEFT_BRACE */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, exports.EnumToken.BlockStartTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.BlockStartTokenType)); break; // '}' case 125 /* TokenMap.RIGHT_BRACE */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, exports.EnumToken.BlockEndTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.BlockEndTokenType)); break; // '(' case 40 /* TokenMap.LEFT_PARENTHESIS */: - if (buffer.length > 0) { - if (buffer[0] === ":" && isPseudo(buffer)) { + if (parseInfo.position < parseInfo.currentPosition) { + if (parseInfo.stream[parseInfo.position - parseInfo.offset] === ":" && isPseudo(parseInfo)) { next(parseInfo); - result.push(yieldResult(buffer, parseInfo, exports.EnumToken.PseudoClassFunctionTokenDefType)); - buffer = ""; + result.push(yieldResult(parseInfo, exports.EnumToken.PseudoClassFunctionTokenDefType)); break; } - else if (isIdent(buffer)) { - const hint = buffer.startsWith("--") + else if (isIdentToken(parseInfo)) { + const hint = startsWith(parseInfo, "--") ? exports.EnumToken.CustomFunctionTokenDefType - : (SymbolsMapTokens[buffer.toLowerCase() + "("] ?? exports.EnumToken.FunctionTokenDefType); - result.push(yieldResult(buffer, parseInfo, hint)); + : (SymbolsMapTokens[parseInfo.stream + .slice(parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset) + .toLowerCase() + "("] ?? exports.EnumToken.FunctionTokenDefType); + result.push(yieldResult(parseInfo, hint)); next(parseInfo); - buffer = ""; + // consume '(' + parseInfo.position = parseInfo.currentPosition; if (hint === exports.EnumToken.UrlFunctionTokenDefType) { - buffer = ""; - value = peek(parseInfo); // consume an while (isWhiteSpace(peek(parseInfo).charCodeAt(0))) { - // buffer += next(parseInfo); next(parseInfo); - // charCode = value.charCodeAt(0); } - value = peek(parseInfo); + charCode = peek(parseInfo).charCodeAt(0); let values = null; - if (value == '"' || value == "'") { + if (charCode == 34 /* TokenMap.DOUBLE_QUOTE */ || charCode == 39 /* TokenMap.SINGLE_QUOTE */) { values = consumeString(parseInfo); } else { do { - buffer += next(parseInfo); - value = peek(parseInfo); - charCode = value.charCodeAt(0); + next(parseInfo); + // value = peek(parseInfo); + charCode = peek(parseInfo).charCodeAt(0); } while ( // !(value === "/" && match(parseInfo, "/*") && - value !== ")" && - value !== ""); + charCode !== 41 /* TokenMap.RIGHT_PARENTHESIS */ && + parseInfo.currentPosition < endPosition); } if (values != null) { - if (peek(parseInfo) === "") { + // NaN is not equal to NaN + if ((charCode = peek(parseInfo).charCodeAt(0)) != charCode) { for (let i = 0; i < values.length; i++) { values[i].token.typ = exports.EnumToken.BadUrlTokenType; } } result.push(...values); } - else if (buffer.length > 0) { - result.push(yieldResult(buffer.trimEnd(), parseInfo, - // buffer.length > 0 - peek(parseInfo) === "" || !isURLToken(buffer) + else if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo, + // parseInfo.position < parseInfo.currentPosition + (charCode = peek(parseInfo).charCodeAt(0)) != charCode || !isURLToken(parseInfo) ? exports.EnumToken.BadUrlTokenType : exports.EnumToken.UrlTokenTokenType)); - buffer = ""; } } break; } } - result.push(yieldResult(next(parseInfo), parseInfo, exports.EnumToken.StartParensTokenType)); - buffer = ""; + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.StartParensTokenType)); break; // ')' case 41 /* TokenMap.RIGHT_PARENTHESIS */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, exports.EnumToken.EndParensTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.EndParensTokenType)); break; // '[' case 91 /* TokenMap.LEFT_BRACKETS */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, exports.EnumToken.AttrStartTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.AttrStartTokenType)); break; // ']' case 93 /* TokenMap.RIGHT_BRACKETS */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, exports.EnumToken.AttrEndTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.AttrEndTokenType)); break; case 59 /* TokenMap.SEMICOLON */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + // console.error(parseInfo.stream.length > parseInfo.currentPosition - parseInfo.offset, parseInfo.position < parseInfo.currentPosition, parseInfo.currentPosition - parseInfo.offset, endPosition, parseInfo.stream.slice(parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset), parseInfo.stream.length) + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, exports.EnumToken.SemiColonTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.SemiColonTokenType)); break; case 58 /* TokenMap.COLON */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + // console.error(parseInfo.currentPosition - parseInfo.offset, parseInfo.position - parseInfo.offset, parseInfo.stream.length, parseInfo.stream.slice(parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset)); + result.push(yieldResult(parseInfo)); } next(parseInfo); if (peek(parseInfo).charCodeAt(0) == 58 /* TokenMap.COLON */) { - result.push(yieldResult(value + next(parseInfo), parseInfo, exports.EnumToken.DoubleColonTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.DoubleColonTokenType)); break; } - result.push(yieldResult(value, parseInfo, exports.EnumToken.ColonTokenType)); + result.push(yieldResult(parseInfo, exports.EnumToken.ColonTokenType)); break; // \n \r \f \v \t space case 0x9: @@ -22436,205 +22582,203 @@ function tokenize(parseInfo, yieldEOFToken = true) { case 0xd: case 0x2028: case 0x2029: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - buffer += next(parseInfo); + next(parseInfo); nextCharCode = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset).charCodeAt(0); while (nextCharCode == 0x20 || (nextCharCode >= 0x9 && nextCharCode <= 0xd) || nextCharCode == 0x2028 || nextCharCode == 0x2029) { - value += next(parseInfo); + next(parseInfo); nextCharCode = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset).charCodeAt(0); } - result.push(yieldResult(value, parseInfo, exports.EnumToken.WhitespaceTokenType)); - buffer = ""; + result.push(yieldResult(parseInfo, exports.EnumToken.WhitespaceTokenType)); break; case 44 /* TokenMap.COMMA */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, exports.EnumToken.CommaTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.CommaTokenType)); break; case 36 /* TokenMap.DOLLAR */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } if (match(parseInfo, "$=")) { - result.push(yieldResult(next(parseInfo, 2), parseInfo, exports.EnumToken.EndMatchTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, exports.EnumToken.EndMatchTokenType)); break; } - buffer += next(parseInfo); + next(parseInfo); break; case 126 /* TokenMap.TILDA */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } if (match(parseInfo, "~=")) { - result.push(yieldResult(next(parseInfo, 2), parseInfo, exports.EnumToken.IncludeMatchTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, exports.EnumToken.IncludeMatchTokenType)); break; } - result.push(yieldResult(next(parseInfo), parseInfo, exports.EnumToken.Tilda)); - buffer = ""; + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.Tilda)); break; // case '^': case 94 /* TokenMap.CARET */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } if (match(parseInfo, "^=")) { - result.push(yieldResult(next(parseInfo, 2), parseInfo, exports.EnumToken.StartMatchTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, exports.EnumToken.StartMatchTokenType)); break; } - buffer += next(parseInfo); + next(parseInfo); break; case 42 /* TokenMap.STAR */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } if (match(parseInfo, "*=")) { - result.push(yieldResult(next(parseInfo, 2), parseInfo, exports.EnumToken.ContainMatchTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, exports.EnumToken.ContainMatchTokenType)); break; } - result.push(yieldResult(next(parseInfo), parseInfo, exports.EnumToken.Star)); - buffer = ""; + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.Star)); break; case 38 /* TokenMap.AMPERSAND */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, exports.EnumToken.NestingSelectorTokenType)); - buffer = ""; + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.NestingSelectorTokenType)); break; case 124 /* TokenMap.PIPE */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } // '||' if (match(parseInfo, "||")) { - result.push(yieldResult(next(parseInfo, 2), parseInfo, exports.EnumToken.ColumnCombinatorTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, exports.EnumToken.ColumnCombinatorTokenType)); break; } else if (match(parseInfo, "|=")) { - result.push(yieldResult(next(parseInfo, 2), parseInfo, exports.EnumToken.DashMatchTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, exports.EnumToken.DashMatchTokenType)); break; } - result.push(yieldResult(next(parseInfo), parseInfo, exports.EnumToken.Pipe)); - buffer = ""; + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.Pipe)); break; case 33 /* TokenMap.EXCLAMATION */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } if (match(parseInfo, "!important")) { - result.push(yieldResult(next(parseInfo, 10), parseInfo, exports.EnumToken.ImportantTokenType)); - buffer = ""; + next(parseInfo, 10); + result.push(yieldResult(parseInfo, exports.EnumToken.ImportantTokenType)); break; } - buffer += next(parseInfo); + next(parseInfo); break; case 47 /* TokenMap.SLASH */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } if (!match(parseInfo, "/*")) { - result.push(yieldResult(next(parseInfo), parseInfo, SymbolsMapTokens[value])); + next(parseInfo); + result.push(yieldResult(parseInfo, SymbolsMapTokens[parseInfo.stream.slice(parseInfo.position, parseInfo.currentPosition)])); break; } - buffer += next(parseInfo, 2); - while ((value = next(parseInfo))) { - if (value == "*") { - buffer += value; + next(parseInfo, 2); + while ((charCode = next(parseInfo).charCodeAt(0)) == charCode) { + if (charCode == 42 /* TokenMap.STAR */) { if (match(parseInfo, "/")) { - result.push(yieldResult(buffer + next(parseInfo), parseInfo, exports.EnumToken.CommentTokenType)); - buffer = ""; + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.CommentTokenType)); break; } } - else { - buffer += value; - } + // else { + // buffer += value; + // } } - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo, exports.EnumToken.BadCommentTokenType)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo, exports.EnumToken.BadCommentTokenType)); } break; case 62 /* TokenMap.GREATERTHAN */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } if (match(parseInfo, ">=")) { - result.push(yieldResult(next(parseInfo, 2), parseInfo, exports.EnumToken.GteTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, exports.EnumToken.GteTokenType)); break; } - result.push(yieldResult(next(parseInfo), parseInfo, exports.EnumToken.GtTokenType)); - buffer = ""; + next(parseInfo); + result.push(yieldResult(parseInfo, exports.EnumToken.GtTokenType)); break; case 60 /* TokenMap.LOWERTHAN */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } if (match(parseInfo, "<=")) { - result.push(yieldResult(next(parseInfo, 2), parseInfo, exports.EnumToken.LteTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, exports.EnumToken.LteTokenType)); break; } - buffer += next(parseInfo); + next(parseInfo); if (match(parseInfo, "!--")) { - buffer += next(parseInfo, 3); - while ((value = next(parseInfo))) { - buffer += value; - if (value == "-" && match(parseInfo, "->")) { + next(parseInfo, 3); + while ((charCode = next(parseInfo).charCodeAt(0)) == charCode) { + if (charCode == 45 /* TokenMap.MINUS */ && match(parseInfo, "->")) { break; } } - if (value === "") { - result.push(yieldResult(buffer, parseInfo, exports.EnumToken.BadCdoTokenType)); + if (parseInfo.currentPosition >= endPosition) { + result.push(yieldResult(parseInfo, exports.EnumToken.BadCdoTokenType)); } else { - result.push(yieldResult(buffer + next(parseInfo, 2), parseInfo, exports.EnumToken.CDOCOMMTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, exports.EnumToken.CDOCOMMTokenType)); } - buffer = ""; } break; case 35 /* TokenMap.HASH */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - buffer += next(parseInfo); + next(parseInfo); break; case 92 /* TokenMap.REVERSE_SOLIDUS */: + if (!yieldEOFToken && parseInfo.stream.length == parseInfo.currentPosition - parseInfo.offset + 1) { + break; + } + // console.error('reverse solidus', parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset, parseInfo.stream.length, parseInfo.stream.slice(parseInfo.position, parseInfo.currentPosition)); next(parseInfo); // EOF if (!peek(parseInfo)) { + if (!yieldEOFToken) { + break; + } // end of stream ignore \\ - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } break; } - buffer += value + next(parseInfo); + next(parseInfo); break; case 39 /* TokenMap.SINGLE_QUOTE */: case 34 /* TokenMap.DOUBLE_QUOTE */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } result.push(...consumeString(parseInfo)); break; @@ -22642,30 +22786,31 @@ function tokenize(parseInfo, yieldEOFToken = true) { const codepoint = parseInfo.stream .charAt(parseInfo.currentPosition - parseInfo.offset + 1) .charCodeAt(0); - if (!isDigit(codepoint) && buffer !== "") { - result.push(yieldResult(buffer, parseInfo)); - buffer = next(parseInfo, 2); + if (!isDigit(codepoint) && parseInfo.position !== parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); + next(parseInfo, 2); break; } - buffer += next(parseInfo); + next(parseInfo); break; default: - buffer += next(parseInfo); + next(parseInfo); break; } - if (!yieldEOFToken && endPosition <= parseInfo.stream.length - parseInfo.currentPosition + parseInfo.offset) { + if (!yieldEOFToken && endPosition <= parseInfo.currentPosition - parseInfo.offset + 1) { break; } } if (yieldEOFToken) { - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); + // console.error(parseInfo.stream.length > parseInfo.currentPosition - parseInfo.offset, parseInfo.position < parseInfo.currentPosition, parseInfo.currentPosition - parseInfo.offset, parseInfo.position - parseInfo.offset, parseInfo.stream.length); + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult("", parseInfo, exports.EnumToken.EOFTokenType)); - } - else { - parseInfo.buffer = buffer; + result.push(yieldResult(parseInfo, exports.EnumToken.EOFTokenType)); } + // else { + // // parseInfo.buffer = buffer; + // } parseInfo.time += performance.now() - startTime; return result; } @@ -22677,19 +22822,17 @@ function tokenize(parseInfo, yieldEOFToken = true) { async function* tokenizeStream(input, parseInfo) { const decoder = new TextDecoder("utf-8"); const reader = input.getReader(); + parseInfo.stream = ""; while (true) { const { done, value } = await reader.read(); const stream = ArrayBuffer.isView(value) ? decoder.decode(value, { stream: true }) : value; if (!done) { parseInfo.source.append(stream); - if (typeof parseInfo.stream != "string") { - parseInfo.stream = stream; - } - else { - parseInfo.stream = (parseInfo.stream.slice(parseInfo.currentPosition - parseInfo.offset) + - stream); - } - parseInfo.offset = parseInfo.currentPosition; + parseInfo.stream = (parseInfo.stream.slice(parseInfo.position - parseInfo.offset) + stream); + parseInfo.offset = parseInfo.offset = parseInfo.position; + } + else { + parseInfo.stream = ""; } yield* tokenize(parseInfo, done); if (done) { @@ -22761,9 +22904,12 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent, co if (rules.includes(replacement.typ) && !Array.isArray(replacement[TOKENS])) { replacement[TOKENS] = parseString(replacement.typ == exports.EnumToken.RuleNodeType || replacement.typ === exports.EnumToken.KeyframesRuleNodeType ? replacement.sel - : replacement.nam); + : // @ts-ignore + replacement.nam); } - const result = feature.run(replacement, options2, parent[PARENT] ?? ast, context, exports.FeatureWalkMode.Pre); + const result = feature.run(replacement, options2, + // @ts-ignore + parent[PARENT] ?? ast, context, exports.FeatureWalkMode.Pre); if (result != null) { replacement = result; } @@ -22772,9 +22918,12 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent, co (!Array.isArray(replacement) || replacement.length > 0) && replacement != parent && parent[PARENT] != null) { + // @ts-ignore replaceNodeOrValue(parent[PARENT], parent, replacement); } + // @ts-ignore if (replacement.chi != null) { + // @ts-ignore for (const node of replacement.chi) { node[PARENT] = replacement; parents.add(node); @@ -22801,7 +22950,9 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent, co (feature.accept != null && !feature.accept.has(parent.typ))) { continue; } - const result = feature.run(replacement, options2, parent[PARENT] ?? ast, context, exports.FeatureWalkMode.Post); + const result = feature.run(replacement, options2, + // @ts-ignore + parent[PARENT] ?? ast, context, exports.FeatureWalkMode.Post); if (result != null) { replacement = result; } @@ -22814,7 +22965,9 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent, co // @ts-ignore replaceNodeOrValue(parent[PARENT], parent, replacement); } + // @ts-ignore if (replacement.chi != null) { + // @ts-ignore for (const node of replacement.chi) { node[PARENT] = replacement; parents.add(node); @@ -22845,6 +22998,7 @@ function transformAtRuleMediaPrelude(values) { values[values.indexOf(value)] = value.l; } else { + // @ts-ignore replaceNodeOrValue(parent, value, value.l); // @ts-ignore value = value.l; @@ -22901,9 +23055,11 @@ function transformAtRuleMediaPrelude(values) { // @ts-expect-error const p = parents?.[parents?.indexOf?.(parent) + 1]; if (p != null) { + // @ts-ignore replaceNodeOrValue(p, parent, replacement); } else { + // @ts-ignore values.splice(values.indexOf(parent), 1, replacement); } hasUpdates = true; @@ -23017,6 +23173,7 @@ function doMinify(ast, options = {}, recursive = false, errors, nestingContent, continue; } while (previous?.typ === exports.EnumToken.CommentNodeType) { + // @ts-ignore previous = ast.chi[--nodeIndex]; } node = ast.chi[i]; @@ -23039,6 +23196,7 @@ function doMinify(ast, options = {}, recursive = false, errors, nestingContent, // do not merge keyframes // https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@keyframes#resolving_duplicates previous.chi.push(...node.chi); + // @ts-ignore ast.chi.splice(i, 1); previous = ast?.chi?.[nodeIndex] ?? null; i = nodeIndex; @@ -23111,6 +23269,7 @@ function doMinify(ast, options = {}, recursive = false, errors, nestingContent, else if (ast.typ === node.typ && ast.nam === node.nam && ast.val === node.val) { + // @ts-ignore replaceNodeOrValue(ast, node, node.chi); i--; continue; @@ -23204,7 +23363,9 @@ function doMinify(ast, options = {}, recursive = false, errors, nestingContent, ":is(" + node[OPTIMIZED].selector.reduce(reducer, []).join(",") + ")"; - const sel2 = node[OPTIMIZED].selector.reduce((acc, curr) => (acc.length > 0 ? acc + "," : "") + node[OPTIMIZED].optimized[0] + curr.join(""), ""); + const sel2 = node[OPTIMIZED].selector.reduce((acc, curr) => + // @ts-ignore + (acc.length > 0 ? acc + "," : "") + node[OPTIMIZED].optimized[0] + curr.join(""), ""); node.sel = sel1.length < sel2.length ? sel1 : sel2; node[TOKENS] = null; } @@ -23284,7 +23445,9 @@ function doMinify(ast, options = {}, recursive = false, errors, nestingContent, ":is(" + node[OPTIMIZED].selector.reduce(reducer, []).join(",") + ")"; - const sel2 = node[OPTIMIZED].selector.reduce((acc, curr) => (acc.length > 0 ? acc + "," : "") + node[OPTIMIZED].optimized[0] + curr.join(""), ""); + const sel2 = node[OPTIMIZED].selector.reduce((acc, curr) => + // @ts-ignore + (acc.length > 0 ? acc + "," : "") + node[OPTIMIZED].optimized[0] + curr.join(""), ""); node.sel = sel1.length < sel2.length ? sel1 : sel2; node[TOKENS] = null; } @@ -23295,11 +23458,14 @@ function doMinify(ast, options = {}, recursive = false, errors, nestingContent, curr.join(""), ""); node[TOKENS] = null; } + // @ts-ignore } else if (node[OPTIMIZED]?.optimized.length > 0) { + // @ts-ignore const sel = node[OPTIMIZED].optimized.join(""); if (sel.length < node.sel.length) { node.sel = sel; + // @ts-ignore node[RAW] = [node[OPTIMIZED].optimized.slice()]; node[TOKENS] = null; } @@ -23324,9 +23490,12 @@ function doMinify(ast, options = {}, recursive = false, errors, nestingContent, if (((node.typ === exports.EnumToken.RuleNodeType || node.typ === exports.EnumToken.KeyframesRuleNodeType) && node.sel === previous.sel) || + // @ts-ignore (node.typ == exports.EnumToken.AtRuleNodeType && node.nam !== "font-face" && + // @ts-ignore node.nam === previous.nam)) { + // @ts-ignore node.chi.unshift(...previous.chi); doMinify(node, options, recursive, errors, nestingContent, context); ast.chi.splice(nodeIndex, 1); @@ -26161,6 +26330,29 @@ function parseSelector(tokens, context, options, errors) { func.val == ":nth-last-child" || func.val == ":nth-of-type" || func.val == ":nth-last-of-type") { + const list = []; + let index; + for (index = 0; index < func.chi.length; index++) { + if (func.chi[index].typ == exports.EnumToken.CommentTokenType || func.chi[index].typ == exports.EnumToken.WhitespaceTokenType) { + continue; + } + if (func.chi[index].typ == exports.EnumToken.IdenTokenType && equalsIgnoreCase('of', func.chi[index].val)) { + index--; + break; + } + list.push(func.chi[index]); + } + if (list.length == 3) { + if (list[0].typ == exports.EnumToken.IdenTokenType && ('n' == list[0].val || '-n' == list[0].val || '+n' == list[0].val)) { + if (list[1].typ == exports.EnumToken.NextSiblingCombinatorTokenType) { + if (list[2].typ == exports.EnumToken.NumberTokenType && (0 == list[2].val)) { + list[0].val = 'n'; + func.chi.splice(0, index, list[0]); + break; + } + } + } + } const token = func.chi.find((t) => t.typ != exports.EnumToken.WhitespaceTokenType && t.typ != exports.EnumToken.CommentTokenType); if (token?.typ == exports.EnumToken.IdenTokenType || token?.typ == exports.EnumToken.LiteralTokenType) { if (token.typ == exports.EnumToken.IdenTokenType && @@ -27976,7 +28168,8 @@ function parseAtRuleContainerQueryList(stream, context, options = {}) { success = false; errors.push({ action: "drop", - node: options.source.getSourceLocation(stream[i][LOC].sta), + node: stream[i], + location: options.source.getSourceLocation(stream[i][LOC].sta), message: ` is not allowed outside of parentheses`, }); break; @@ -29035,7 +29228,9 @@ function doParseSync(iter, options = {}) { break; } } + // @ts-ignore if (nodes[i].chi != null) { + // @ts-ignore subNodes.push(...nodes[i].chi); } if (subNodes.length > 0) { @@ -29154,6 +29349,7 @@ function doParseSync(iter, options = {}) { } } if (node != nodes[i]) { + // @ts-ignore replaceNodeOrValue(nodes[i][PARENT], nodes[i], node); } } @@ -29273,6 +29469,7 @@ function doParseSync(iter, options = {}) { throw new Error("css variable import not supported by parseSync() or transformSync(). use parse() or transform() instead.\nat " + options.source.getSourceLocation(node[LOC].sta).join(":")); } + // @ts-ignore if (node.typ == exports.EnumToken.CssVariableDeclarationMapTokenType) { const from = node.from.find((t) => t.typ == exports.EnumToken.IdenTokenType || isIdentColor(t)); if (!(from.val in cssVariablesMap)) { @@ -29547,6 +29744,7 @@ function doParseSync(iter, options = {}) { } else if ((value.typ == exports.EnumToken.IdenTokenType || isIdentColor(value)) && value.val in importedCssVariables) { + // @ts-ignore replaceNodeOrValue(parent, value, importedCssVariables[value.val].val); } } @@ -29622,7 +29820,7 @@ function doParseSync(iter, options = {}) { } if (moduleSettings.scoped & exports.ModuleScopeEnumOptions.Pure) { if (!hasIdOrClass) { - throw new Error(`pure module: No id or class found in selector '${node.sel}' at '${node[LOC]?.src ?? ""}':${node[LOC]?.sta?.lin ?? ""}:${node[LOC]?.sta?.col ?? ""}`); + throw new Error(`pure module: No id or class found in selector '${node.sel}' at '${options.source.getOffsets(node[LOC]?.sta).join(":")}'`); } } node.sel = ""; @@ -29908,6 +30106,7 @@ async function doParse(iter, options = {}) { stats.nodesCount += root.stats.nodesCount; stats.tokensCount += root.stats.tokensCount; stats.imports.push(root.stats); + // @ts-ignore node[PARENT].chi.splice(node[PARENT].chi.indexOf(node), 1, ...root.ast.chi); if (root.errors.length > 0) { errors.push(...root.errors); @@ -29954,7 +30153,9 @@ async function doParse(iter, options = {}) { break; } } + // @ts-ignore if (nodes[i].chi != null) { + // @ts-ignore subNodes.push(...nodes[i].chi); } if (subNodes.length > 0) { @@ -30076,6 +30277,7 @@ async function doParse(iter, options = {}) { } } if (node != nodes[i]) { + // @ts-ignore replaceNodeOrValue(nodes[i][PARENT], nodes[i], node); } } @@ -30220,6 +30422,7 @@ async function doParse(iter, options = {}) { parent.chi.splice(parent.chi.indexOf(node), 1); continue; } + // @ts-ignore if (node.typ == exports.EnumToken.CssVariableDeclarationMapTokenType) { const from = node.from.find((t) => t.typ == exports.EnumToken.IdenTokenType || isIdentColor(t)); if (!(from.val in cssVariablesMap)) { @@ -30264,6 +30467,7 @@ async function doParse(iter, options = {}) { let result = moduleSettings.scoped & exports.ModuleScopeEnumOptions.Global ? node.nam : moduleSettings.generateScopedName(node.nam, moduleSettings.filePath, moduleSettings.pattern, moduleSettings.hashLength); + // @ts-ignore let value = result instanceof Promise ? await result : result; mapping[node.nam] = "--" + @@ -30571,30 +30775,13 @@ async function doParse(iter, options = {}) { } for (const { value, parent } of walkValues(node.val, node)) { if (value.typ == exports.EnumToken.DashedIdenTokenType) { - // if (!((value as DashedIdentToken).val in mapping)) { - // const result = - // moduleSettings.scoped! & ModuleScopeEnumOptions.Global - // ? (value as DashedIdentToken).val - // : moduleSettings.generateScopedName!( - // (value as DashedIdentToken).val, - // moduleSettings.filePath as string, - // moduleSettings.pattern as string, - // moduleSettings.hashLength, - // ); - // let val: string = result instanceof Promise ? await result : result; - // mapping[(value as DashedIdentToken).val] = - // "--" + - // (moduleSettings.naming! & ModuleCaseTransformEnum.DashCaseOnly || - // moduleSettings.naming! & ModuleCaseTransformEnum.CamelCaseOnly - // ? getKeyName(val, moduleSettings.naming as ModuleCaseTransformEnum) - // : val); - // revMapping[mapping[(value as DashedIdentToken).val]] = (value as DashedIdentToken).val; - // } value.val = mapping[value.val]; } else if ((value.typ == exports.EnumToken.IdenTokenType || isIdentColor(value)) && value.val in importedCssVariables) { - replaceNodeOrValue(parent, value, importedCssVariables[value.val].val); + replaceNodeOrValue( + // @ts-ignore + parent, value, importedCssVariables[value.val].val); } } } @@ -30672,7 +30859,7 @@ async function doParse(iter, options = {}) { } if (moduleSettings.scoped & exports.ModuleScopeEnumOptions.Pure) { if (!hasIdOrClass) { - throw new Error(`pure module: No id or class found in selector '${node.sel}' at '${node[LOC]?.src ?? ""}':${node[LOC]?.sta?.lin ?? ""}:${node[LOC]?.sta?.col ?? ""}`); + throw new Error(`pure module: No id or class found in selector '${node.sel}' at '${(options.source?.getOffsets?.(node[LOC]?.sta) ?? []).join(":")}'`); } } node.sel = ""; @@ -31292,10 +31479,13 @@ function parseAtRule(stream, context, options, errors, parseAsBlock = null) { let definedAfterLastElse = false; if (sibling == null || sibling.typ !== exports.EnumToken.AtRuleNodeType) { missingWhen = true; + // @ts-expect-error } else if (sibling.nam !== "when") { + // @ts-expect-error if (sibling.nam !== "else") { missingWhen = true; + // @ts-expect-error } else if (sibling.val === "") { definedAfterLastElse = true; @@ -31990,6 +32180,12 @@ function parseResult(result, options) { } return result; } +/** + * + * @param options + * @param prefix + * @private + */ function validateSyncArguments(options, prefix = "options.") { const args = Object.entries(options); let i; @@ -32006,6 +32202,52 @@ function validateSyncArguments(options, prefix = "options.") { } } +/** + * set node property + * @param node + * @param property + * @param value + */ +function setNodeProperty(node, property, value) { + switch (property) { + case "location": + node[LOC] = value; + break; + case "state": + node[STATE] = value; + break; + case "errors": + node[ERRORS] = value; + break; + case "tokens": + node[TOKENS] = value; + break; + case "parent": + node[PARENT] = value; + break; + } +} +/** + * get node property + * @param node + * @param property + * @returns + */ +function getNodeProperty(node, property) { + switch (property) { + case "location": + return node[LOC]; + case "state": + return node[STATE]; + case "errors": + return node[ERRORS]; + case "tokens": + return node[TOKENS]; + case "parent": + return node[PARENT]; + } +} + /** * Load file or url * @param url @@ -32089,7 +32331,7 @@ function render(data, options = {}, mapping) { return doRender(data, Object.assign(options, { resolve, dirname, cwd: options.cwd ?? node_path.resolve() }), mapping); } /** - * Parse css file + * Parse CSS file * @param file url or path * @param options * @param asStream load file as stream @@ -32115,9 +32357,8 @@ function render(data, options = {}, mapping) { */ const parseFile = node_util.deprecate(async (file, options = {}, asStream = false) => parse({ file, asStream, ...options }), "parseFile is deprecated, use parse instead as parse({file, asStream, ...options})"); /** - * Parse css + * Parse CSS * @param args - * @private * * Parsing a string * @@ -32174,7 +32415,7 @@ function parseSync(...args) { return !options.module && !options.inputSourceMap && !options.sourcemap ? result : parseResult(result, options); } /** - * Transform css + * Transform CSS * * ```ts * @@ -32186,7 +32427,6 @@ function parseSync(...args) { * ``` * * @param args - * @private */ function transformSync(...args) { let options; @@ -32202,7 +32442,15 @@ function transformSync(...args) { stream = input; } options ??= {}; - options = { minify: true, removeEmpty: true, removeCharset: true, ...options }; + if (options.minify == null) { + options.minify = true; + } + if (options.removeEmpty == null) { + options.removeEmpty = true; + } + if (options.removeCharset == null) { + options.removeCharset = true; + } const startTime = performance.now(); const parseResult = parseSync(stream, options); let mapping = null; @@ -32235,11 +32483,10 @@ function transformSync(...args) { }; } /** - * Parse css + * Parse CSS * @param args * * @throws Error file not found - * @private * * Parsing a string * @@ -32354,7 +32601,7 @@ const transformFile = node_util.deprecate(async (file, options = {}, asStream = ...options, }), "transformFile is deprecated, use transform instead as transform({file, asStream, ...options})"); /** - * Transform css + * Transform CSS * * Parsing a string * @@ -32394,7 +32641,6 @@ const transformFile = node_util.deprecate(async (file, options = {}, asStream = * console.log(result.code); * ``` * @param args - * @private */ async function transform(...args) { let options; @@ -32415,7 +32661,15 @@ async function transform(...args) { } } options ??= {}; - options = { minify: true, removeEmpty: true, removeCharset: true, ...options }; + if (options.minify == null) { + options.minify = true; + } + if (options.removeEmpty == null) { + options.removeEmpty = true; + } + if (options.removeCharset == null) { + options.removeCharset = true; + } const startTime = performance.now(); return parse(stream, options).then((parseResult) => { let mapping = null; @@ -32458,6 +32712,7 @@ exports.find = find; exports.findAll = findAll; exports.findByValue = findByValue; exports.findLast = findLast; +exports.getNodeProperty = getNodeProperty; exports.isOkLabClose = isOkLabClose; exports.load = load; exports.minify = minify; @@ -32471,6 +32726,7 @@ exports.render = render; exports.renderToken = renderValue; exports.replaceNodeOrValue = replaceNodeOrValue; exports.resolve = resolve; +exports.setNodeProperty = setNodeProperty; exports.transform = transform; exports.transformFile = transformFile; exports.transformSync = transformSync; diff --git a/dist/index.d.ts b/dist/index.d.ts index 5519e86f..0228c38e 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -1197,10 +1197,12 @@ export declare interface FunctionToken extends BaseToken { | EnumToken.ImageFunctionTokenType | EnumToken.TimelineFunctionTokenType | EnumToken.TimingFunctionTokenType - | EnumToken.ColorFunctionTokenType + | EnumToken.ColorTokenType | EnumToken.MathFunctionTokenType - | EnumToken.PseudoClassFunctionTokenType - | EnumToken.TransformFunctionTokenType; + | EnumToken.PseudoClassFuncTokenType + | EnumToken.TransformFunctionTokenType + | EnumToken.GeneralEnclosedFunctionTokenType + | EnumToken.WildCardFunctionTokenType; /** * function name */ @@ -2411,7 +2413,7 @@ export declare interface ComposesSelectorToken extends BaseToken { /** * Css variable token */ -export declare interface CssVariableToken$1 extends BaseToken { +export declare interface CssVariableToken extends BaseToken { /** * @inheritdoc */ @@ -2429,7 +2431,7 @@ export declare interface CssVariableToken$1 extends BaseToken { /** * Css variable import token */ -export declare interface CssVariableImportTokenType$1 extends BaseToken { +export declare interface CssVariableImportTokenType extends BaseToken { /** * @inheritdoc */ @@ -2610,7 +2612,7 @@ export declare type Token$1 = | MatchExpressionToken | NameSpaceAttributeToken | ComposesSelectorToken - | CssVariableToken$1 + | CssVariableToken | DashMatchToken | EqualMatchToken | LessThanToken @@ -2670,16 +2672,16 @@ export declare interface BaseToken { * location info * @private */ - [LOC]?: SourceLocation; + [LOC]?: SourceLocation | null; /** * parent node * @private */ - [PARENT]?: AstNode$1; + [PARENT]?: AstNode$1 | Token$1 | null; /** * root node */ - [ROOT]?: AstStyleSheet; + [ROOT]?: AstStyleSheet | null; /** * prelude or selector tokens * @private @@ -2689,12 +2691,12 @@ export declare interface BaseToken { * node state * @private */ - [STATE]?: EnumAstNodeStatus; + [STATE]?: EnumAstNodeStatus | null; /** * node syntax errors * @private */ - [ERRORS]?: ErrorDescription[]; + [ERRORS]?: ErrorDescription[] | null; /** * property name * @private @@ -3049,7 +3051,8 @@ export declare type AstNode$1 = | AstInvalidAtRule | AstInvalidDeclaration | CssVariableToken - | CssVariableImportTokenType; + | CssVariableImportTokenType + | WhitespaceToken; /** * token search result @@ -3962,11 +3965,11 @@ declare class SourceMap { */ private line; /** - * + * Constructor */ constructor(); /** - * + * Constructor * @param sourcemaps */ constructor(sourcemaps: string | SourceMapObject); @@ -3979,17 +3982,20 @@ declare class SourceMap { */ addSourceContent(id: number, fileName: string | null, content: string | null): void; /** - * Add location - * @param maps - * @throws + * Add sourcemap + * @param newLine + * @param newColumn + * @param srcId + * @param ln + * @param col */ add(newLine: number, newColumn: number, srcId: number, ln: number, col: number): void; /** - * Add multiple locations + * Add multiple sourcemaps * @param maps * @throws */ - add(...maps: Array<[number, number, number, number, number]>): void; + add(...maps: Array<[newLine: number, newColumn: number, srcId: number, ln: number, col: number]>): void; /** * compute original positions */ @@ -4141,10 +4147,6 @@ export declare interface PropertyListOptions { */ export declare interface ParseInfo$1 { - /** - * read buffer - */ - buffer: string; /** * stream */ @@ -5862,7 +5864,7 @@ export declare interface ParseResult { * CSS module variables * @private */ - cssModuleVariables?: Record; + cssModuleVariables?: Record; /** * css module import mapping @@ -6464,8 +6466,78 @@ declare function replaceNodeOrValue(parent: BinaryExpressionToken | (AstNode$1 & chi: Token$1[]; } | { val: Token$1[]; +})) | (Token$1 & ({ + chi: Token$1[]; +} | { + val: Token$1[]; })), node: Token$1, replacement: Token$1 | Token$1[]): boolean; +/** + * + * @param node + * @param property + * @param value + */ +declare function setNodeProperty(node: AstNode$1, property: "location", value: SourceLocation): void; +/** + * + * @param node + * @param property + * @param value + */ +declare function setNodeProperty(node: AstNode$1, property: "state", value: EnumAstNodeStatus$1): void; +/** + * + * @param node + * @param property + * @param value + */ +declare function setNodeProperty(node: AstNode$1, property: "errors", value: ErrorDescription$1[]): void; +/** + * + * @param node + * @param property + * @param value + */ +declare function setNodeProperty(node: AstNode$1, property: "tokens", value: Token$1[]): void; +/** + * + * @param node + * @param property + * @param value + */ +declare function setNodeProperty(node: AstNode$1, property: "parent", value: AstNode$1 | Token$1): void; +/** + * + * @param node + * @param property + */ +declare function getNodeProperty(node: AstNode$1, property: "location"): SourceLocation | null; +/** + * + * @param node + * @param property + */ +declare function getNodeProperty(node: AstNode$1, property: "state"): EnumAstNodeStatus$1 | null; +/** + * + * @param node + * @param property + */ +declare function getNodeProperty(node: AstNode$1, property: "errors"): ErrorDescription$1[] | null; +/** + * + * @param node + * @param property + */ +declare function getNodeProperty(node: AstNode$1, property: "tokens"): Token$1[] | null; +/** + * + * @param node + * @param property + */ +declare function getNodeProperty(node: AstNode$1, property: "parent"): AstNode$1 | Token$1 | null; + /** * Load file or url * @param url @@ -6516,7 +6588,7 @@ declare function render(data: AstNode$1, options?: RenderOptions, mapping?: { importMapping: Record> | null; } | null): RenderResult; /** - * Parse css file + * Parse CSS file * @param file url or path * @param options * @param asStream load file as stream @@ -6542,7 +6614,7 @@ declare function render(data: AstNode$1, options?: RenderOptions, mapping?: { */ declare const parseFile: (file: string, options?: ParserOptions, asStream?: boolean) => Promise; /** - * Parse css string + * Parse CSS string * @param stream * @param options * @@ -6560,7 +6632,7 @@ declare const parseFile: (file: string, options?: ParserOptions, asStream?: bool */ declare function parseSync(stream: string, options?: ParserSyncOptions): ParseResult; /** - * Parse css string + * Parse CSS string * @param options * * Parsing a string @@ -6577,7 +6649,7 @@ declare function parseSync(stream: string, options?: ParserSyncOptions): ParseRe */ declare function parseSync(options: ParseInputOptions & ParserSyncOptions): ParseResult; /** - * Transform css + * Transform CSS * @param css * @param options * @@ -6594,7 +6666,7 @@ declare function parseSync(options: ParseInputOptions & ParserSyncOptions): Pars */ declare function transformSync(css: string, options?: TransformSyncOptions): TransformResult; /** - * Transform css + * Transform CSS * @param options * * ```ts @@ -6653,7 +6725,7 @@ declare function transformSync(options: ParseInputOptions & TransformSyncOptions */ declare function parse(stream: string | ReadableStream, options?: ParserOptions): Promise; /** - * Parse css + * Parse CSS * @param options * * @throws Error file not found @@ -6685,7 +6757,7 @@ declare function parse(stream: string | ReadableStream, options?: Pa */ declare function parse(options: ParseInputFileOptions & ParserOptions): Promise; /** - * Parse css + * Parse CSS * @param options * * Parsing a string @@ -6795,7 +6867,7 @@ declare const transformFile: (file: string, options?: TransformOptions, asStream */ declare function transform(css: string | ReadableStream, options?: TransformOptions): Promise; /** - * Transform css + * Transform CSS * @param options * * Parsing a string @@ -6853,5 +6925,5 @@ declare function transform(options: ParseInputStreamOptions & TransformOptions): */ declare function transform(options: ParseInputFileOptions & TransformOptions): Promise; -export { ColorType$1 as ColorType, EnumAstNodeStatus$1 as EnumAstNodeStatus, EnumToken, FeatureWalkMode, ModuleCaseTransformEnum, ModuleScopeEnumOptions, ResponseType$1 as ResponseType, SourceMap, ValidationLevel, WalkerEvent, WalkerOptionEnum, cloneNode, convertColor, dirname, expand, find, findAll, findByValue, findLast, isOkLabClose, load, minify, okLabDistance, parse, parseDeclarations, parseFile, parseString, parseSync, render, renderValue as renderToken, replaceNodeOrValue, resolve, transform, transformFile, transformSync, walk, walkValues }; -export type { AddToken, AndToken, AngleToken, AstAtRule, AstComment, AstDeclaration, AstInvalidAtRule, AstInvalidDeclaration, AstInvalidRule, AstKeyframesAtRule, AstKeyframesRule, AstNode$1 as AstNode, AstNodeStatus, AstRule, AstRuleList, AstStyleSheet, AstValueMatcher, AtRuleToken, AtRuleVisitorHandler, AttrEndToken, AttrStartToken, AttrToken, Background, BackgroundAttachmentMapping, BackgroundPosition, BackgroundPositionClass, BackgroundPositionConstraints, BackgroundPositionMapping, BackgroundProperties, BackgroundRepeat, BackgroundRepeatMapping, BackgroundSize, BackgroundSizeMapping, BadCDOCommentToken, BadCommentToken, BadStringToken, BadUrlToken, BaseToken, BinaryExpressionNode, BinaryExpressionToken, BlockEndToken, BlockStartToken, Border, BorderColor, BorderColorClass, BorderProperties, BorderRadius, CDOCommentToken, ChildCombinatorToken, ClassSelectorToken, ColonToken, ColorToken, ColumnCombinatorToken, CommaToken, CommentToken, ComposesSelectorToken, ConstraintsMapping, ContainMatchToken, ContainerStyleRangeToken, CssVariableImportTokenType$1 as CssVariableImportTokenType, CssVariableMapTokenType, CssVariableToken$1 as CssVariableToken, DashMatchToken, DashedIdentToken, DeclarationVisitorHandler, DelimToken, DescendantCombinatorToken, DimensionToken, DivToken, DoubleColonToken, EOFToken, EndMatchToken, EqualMatchToken, ErrorDescription$1 as ErrorDescription, FlexToken, Font, FontFamily, FontProperties, FontWeight, FontWeightConstraints, FontWeightMapping, FractionToken, FrequencyToken, FunctionDefToken, FunctionImageToken, FunctionToken, FunctionURLToken, GenericVisitorAstNodeHandlerMap, GenericVisitorAstNodeSyncHandlerMap, GenericVisitorAsyncResult, GenericVisitorHandler, GenericVisitorResult, GenericVisitorSyncHandler, GenericVisitorSyncResult, GreaterThanOrEqualToken, GreaterThanToken, GridTemplateFuncToken, HashToken, IdentListToken, IdentToken, IfConditionToken, IfElseConditionToken, ImportantToken, IncludeMatchToken, InvalidAttrToken, InvalidClassSelectorToken, InvalidMediaQueryToken, LengthToken, LessThanOrEqualToken, LessThanToken, LineHeight, ListToken, LiteralToken, LoadResult, Map$1 as Map, MatchExpressionToken, MatchedSelector, MediaFeatureOnlyToken, MediaFeatureToken, MediaQueryConditionToken, MediaQueryUnaryFeatureToken, MediaRangeQueryToken, MinifyFeature, MinifyFeatureOptions, MinifyOptions, ModuleAsyncOptions, ModuleSyncOptions, MulToken, NameSpaceAttributeToken, NestingSelectorToken, NextSiblingCombinatorToken, NotToken, NumberToken, OptimizedSelector, OptimizedSelectorToken, OrToken, Outline, OutlineProperties, ParensEndToken, ParensStartToken, ParensToken, ParseInfo$1 as ParseInfo, ParseInputFileOptions, ParseInputOptions, ParseInputStreamOptions, ParseResult, ParseResultStats, ParseSourceOptions, ParseTokenOptions, ParserOptions, ParserSourceMapOptions, ParserSyncOptions, PercentageToken, Prefix, PropertiesConfig, PropertiesConfigProperties, PropertyListOptions, PropertyMapType, PropertySetType, PropertyType, PseudoClassFunctionToken, PseudoClassToken, PseudoElementToken, PseudoPageToken, PurpleBackgroundAttachment, RawNodeToken, RawSelectorTokens, RenderOptions, RenderResult, ResolutionToken, ResolvedPath, RuleVisitorHandler, SemiColonToken, Separator, ShorthandDef, ShorthandMapType, ShorthandProperties, ShorthandPropertyType, ShorthandType, SinglePropertyType, SinglePropertyTypeMapping, SourceLocation, SourceMapObject, StartMatchToken, StringToken, SubToken, SubsequentCombinatorToken, SupportsQueryConditionToken, SupportsQueryUnaryConditionToken, TimeToken, TimelineFunctionToken, TimingFunctionToken, Token$1 as Token, TokenSearchResult, TokenizeResult, TransformOptions, TransformResult, TransformSyncOptions, UnaryExpression, UnaryExpressionNode, UnclosedStringToken, UniversalSelectorToken, UrlToken, ValidationConfiguration, ValidationMediaFeature, ValidationOptions, ValidationResult, ValidationSelectorOptions, ValidationSyntaxNode, ValidationSyntaxResult, ValidationToken$1 as ValidationToken, Value, ValueVisitorHandler, ValueVisitorSyncHandler, VariableScopeInfo, VisitorNodeMap, VisitorSyncNodeMap, WalkAttributesResult, WalkResult, WalkerFilter, WalkerOption, WalkerOptions, WalkerValueFilter, WhenElseQueryConditionToken, WhenElseUnaryConditionToken, WhitespaceToken, WrappedValuesToken }; +export { ColorType$1 as ColorType, EnumAstNodeStatus$1 as EnumAstNodeStatus, EnumToken, FeatureWalkMode, ModuleCaseTransformEnum, ModuleScopeEnumOptions, ResponseType$1 as ResponseType, SourceMap, ValidationLevel, WalkerEvent, WalkerOptionEnum, cloneNode, convertColor, dirname, expand, find, findAll, findByValue, findLast, getNodeProperty, isOkLabClose, load, minify, okLabDistance, parse, parseDeclarations, parseFile, parseString, parseSync, render, renderValue as renderToken, replaceNodeOrValue, resolve, setNodeProperty, transform, transformFile, transformSync, walk, walkValues }; +export type { AddToken, AndToken, AngleToken, AstAtRule, AstComment, AstDeclaration, AstInvalidAtRule, AstInvalidDeclaration, AstInvalidRule, AstKeyframesAtRule, AstKeyframesRule, AstNode$1 as AstNode, AstNodeStatus, AstRule, AstRuleList, AstStyleSheet, AstValueMatcher, AtRuleToken, AtRuleVisitorHandler, AttrEndToken, AttrStartToken, AttrToken, Background, BackgroundAttachmentMapping, BackgroundPosition, BackgroundPositionClass, BackgroundPositionConstraints, BackgroundPositionMapping, BackgroundProperties, BackgroundRepeat, BackgroundRepeatMapping, BackgroundSize, BackgroundSizeMapping, BadCDOCommentToken, BadCommentToken, BadStringToken, BadUrlToken, BaseToken, BinaryExpressionNode, BinaryExpressionToken, BlockEndToken, BlockStartToken, Border, BorderColor, BorderColorClass, BorderProperties, BorderRadius, CDOCommentToken, ChildCombinatorToken, ClassSelectorToken, ColonToken, ColorToken, ColumnCombinatorToken, CommaToken, CommentToken, ComposesSelectorToken, ConstraintsMapping, ContainMatchToken, ContainerStyleRangeToken, CssVariableImportTokenType, CssVariableMapTokenType, CssVariableToken, DashMatchToken, DashedIdentToken, DeclarationVisitorHandler, DelimToken, DescendantCombinatorToken, DimensionToken, DivToken, DoubleColonToken, EOFToken, EndMatchToken, EqualMatchToken, ErrorDescription$1 as ErrorDescription, FlexToken, Font, FontFamily, FontProperties, FontWeight, FontWeightConstraints, FontWeightMapping, FractionToken, FrequencyToken, FunctionDefToken, FunctionImageToken, FunctionToken, FunctionURLToken, GenericVisitorAstNodeHandlerMap, GenericVisitorAstNodeSyncHandlerMap, GenericVisitorAsyncResult, GenericVisitorHandler, GenericVisitorResult, GenericVisitorSyncHandler, GenericVisitorSyncResult, GreaterThanOrEqualToken, GreaterThanToken, GridTemplateFuncToken, HashToken, IdentListToken, IdentToken, IfConditionToken, IfElseConditionToken, ImportantToken, IncludeMatchToken, InvalidAttrToken, InvalidClassSelectorToken, InvalidMediaQueryToken, LengthToken, LessThanOrEqualToken, LessThanToken, LineHeight, ListToken, LiteralToken, LoadResult, Map$1 as Map, MatchExpressionToken, MatchedSelector, MediaFeatureOnlyToken, MediaFeatureToken, MediaQueryConditionToken, MediaQueryUnaryFeatureToken, MediaRangeQueryToken, MinifyFeature, MinifyFeatureOptions, MinifyOptions, ModuleAsyncOptions, ModuleSyncOptions, MulToken, NameSpaceAttributeToken, NestingSelectorToken, NextSiblingCombinatorToken, NotToken, NumberToken, OptimizedSelector, OptimizedSelectorToken, OrToken, Outline, OutlineProperties, ParensEndToken, ParensStartToken, ParensToken, ParseInfo$1 as ParseInfo, ParseInputFileOptions, ParseInputOptions, ParseInputStreamOptions, ParseResult, ParseResultStats, ParseSourceOptions, ParseTokenOptions, ParserOptions, ParserSourceMapOptions, ParserSyncOptions, PercentageToken, Prefix, PropertiesConfig, PropertiesConfigProperties, PropertyListOptions, PropertyMapType, PropertySetType, PropertyType, PseudoClassFunctionToken, PseudoClassToken, PseudoElementToken, PseudoPageToken, PurpleBackgroundAttachment, RawNodeToken, RawSelectorTokens, RenderOptions, RenderResult, ResolutionToken, ResolvedPath, RuleVisitorHandler, SemiColonToken, Separator, ShorthandDef, ShorthandMapType, ShorthandProperties, ShorthandPropertyType, ShorthandType, SinglePropertyType, SinglePropertyTypeMapping, SourceLocation, SourceMapObject, StartMatchToken, StringToken, SubToken, SubsequentCombinatorToken, SupportsQueryConditionToken, SupportsQueryUnaryConditionToken, TimeToken, TimelineFunctionToken, TimingFunctionToken, Token$1 as Token, TokenSearchResult, TokenizeResult, TransformOptions, TransformResult, TransformSyncOptions, UnaryExpression, UnaryExpressionNode, UnclosedStringToken, UniversalSelectorToken, UrlToken, ValidationConfiguration, ValidationMediaFeature, ValidationOptions, ValidationResult, ValidationSelectorOptions, ValidationSyntaxNode, ValidationSyntaxResult, ValidationToken$1 as ValidationToken, Value, ValueVisitorHandler, ValueVisitorSyncHandler, VariableScopeInfo, VisitorNodeMap, VisitorSyncNodeMap, WalkAttributesResult, WalkResult, WalkerFilter, WalkerOption, WalkerOptions, WalkerValueFilter, WhenElseQueryConditionToken, WhenElseUnaryConditionToken, WhitespaceToken, WrappedValuesToken }; diff --git a/dist/lib/ast/clone.js b/dist/lib/ast/clone.js index 3842583d..37b9563a 100644 --- a/dist/lib/ast/clone.js +++ b/dist/lib/ast/clone.js @@ -14,23 +14,28 @@ function cloneNode(node, cloneChildren = false, cloneMap = null) { cloneMap?.set?.(node, clone); for (const [name, value] of Object.entries(node)) { if (value == null || typeof value != "object") { + // @ts-ignore clone[name] = value; } else if (Array.isArray(value)) { + // @ts-ignore clone[name] = []; if (cloneChildren || name !== checkNode) { for (const c of value) { const newObj = cloneNode(c, cloneChildren, cloneMap); cloneMap?.set?.(c, newObj); + // @ts-ignore clone[name].push(newObj); } } } else { + // @ts-ignore clone[name] = { ...value }; } } for (const symbol of Object.getOwnPropertySymbols(node)) { + // @ts-ignore clone[symbol] = node[symbol]; } return clone; diff --git a/dist/lib/ast/features/calc.js b/dist/lib/ast/features/calc.js index c7d639f0..53efdac9 100644 --- a/dist/lib/ast/features/calc.js +++ b/dist/lib/ast/features/calc.js @@ -109,7 +109,8 @@ class ComputeCalcExpressionFeature { // @ts-ignore const children = parent.typ == EnumToken.DeclarationNodeType ? parent.val - : parent.chi; + : // @ts-ignore + parent.chi; if (values.length == 1 && values[0].typ != EnumToken.BinaryExpressionTokenType) { for (let i = 0; i < children.length; i++) { if (children[i] == value) { diff --git a/dist/lib/ast/features/if.js b/dist/lib/ast/features/if.js index 6977cf34..5c58b2a5 100644 --- a/dist/lib/ast/features/if.js +++ b/dist/lib/ast/features/if.js @@ -29,6 +29,7 @@ function substituteIfElseNode(declaration, node, wrapper, parentWrapper, cache) EnumToken.SemiColonTokenType ? trimArray(node.r.r.slice(0, -1)) : node.r.r); + // @ts-expect-error if (targetParentWrapper.typ != EnumToken.DeclarationNodeType) { let index = targetParentWrapper.chi.indexOf(targetWrapper); if (index != -1) { @@ -50,6 +51,7 @@ function substituteIfElseNode(declaration, node, wrapper, parentWrapper, cache) .r.r.slice(0, -1)) : siblingWrapper.chi[k] .r.r); + // @ts-ignore cache.add(siblingWrapper.chi[k].l); } } @@ -73,7 +75,9 @@ function substituteIfElseNode(declaration, node, wrapper, parentWrapper, cache) } if (left.typ === EnumToken.IdenTokenType && equalsIgnoreCase("else", left.val)) { clonedDeclaration = cloneNode(declaration, true, nodeMap); - replaceNodeOrValue(nodeMap.get(parentWrapper), nodeMap.get(targetWrapper.typ === EnumToken.DeclarationNodeType ? node : targetWrapper), node.r.at(-1)?.typ === EnumToken.SemiColonTokenType ? trimArray(node.r.slice(0, -1)) : node.r); + replaceNodeOrValue(nodeMap.get(parentWrapper), + // @ts-expect-error + nodeMap.get(targetWrapper.typ === EnumToken.DeclarationNodeType ? node : targetWrapper), node.r.at(-1)?.typ === EnumToken.SemiColonTokenType ? trimArray(node.r.slice(0, -1)) : node.r); result.push(clonedDeclaration); } else if (left?.typ === EnumToken.WhenElseFunctionTokenType) { @@ -140,17 +144,27 @@ function processNode(declarationNode, cache) { const parentWrapper = node.parent ?? parents.find((node) => !nodeMatcher(node)); if (node.node.typ === EnumToken.WildCardFunctionTokenType) { for (i = 0; i < node.node.chi.length; i++) { - stack.push(...substituteIfElseNode(declaration, node.node.chi[i], node.node, parentWrapper, cache)); + stack.push( + // @ts-expect-error + ...substituteIfElseNode( + // @ts-expect-error + declaration, node.node.chi[i], node.node, parentWrapper, cache)); } } else { - stack.push(...substituteIfElseNode(declaration, node.node, parentWrapper, parents[parents.indexOf(parentWrapper) + 1] ?? declaration, cache)); + stack.push( + // @ts-expect-error + ...substituteIfElseNode( + // @ts-expect-error + declaration, node.node, parentWrapper, parents[parents.indexOf(parentWrapper) + 1] ?? declaration, cache)); } } if (result.length > 0) { + // @ts-expect-error replaceNodeOrValue(declarationNode[PARENT], declarationNode, result); } // else remove node? + // @ts-expect-error return result; } class ExpandIfFeature { diff --git a/dist/lib/ast/find.js b/dist/lib/ast/find.js index 99a59a98..8c46228f 100644 --- a/dist/lib/ast/find.js +++ b/dist/lib/ast/find.js @@ -88,6 +88,7 @@ function findByValue(ast, matcher) { } for (const { value, parent, root: rootNode, parents } of walkValues(source, node)) { if (matcher(value, node)) { + // @ts-ignore return { node, value: { node: value, parent, root: rootNode, parents } }; } } diff --git a/dist/lib/ast/minify.js b/dist/lib/ast/minify.js index 0cada89c..fb0a66aa 100644 --- a/dist/lib/ast/minify.js +++ b/dist/lib/ast/minify.js @@ -75,9 +75,12 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent, co if (rules.includes(replacement.typ) && !Array.isArray(replacement[TOKENS])) { replacement[TOKENS] = parseString(replacement.typ == EnumToken.RuleNodeType || replacement.typ === EnumToken.KeyframesRuleNodeType ? replacement.sel - : replacement.nam); + : // @ts-ignore + replacement.nam); } - const result = feature.run(replacement, options2, parent[PARENT] ?? ast, context, FeatureWalkMode.Pre); + const result = feature.run(replacement, options2, + // @ts-ignore + parent[PARENT] ?? ast, context, FeatureWalkMode.Pre); if (result != null) { replacement = result; } @@ -86,9 +89,12 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent, co (!Array.isArray(replacement) || replacement.length > 0) && replacement != parent && parent[PARENT] != null) { + // @ts-ignore replaceNodeOrValue(parent[PARENT], parent, replacement); } + // @ts-ignore if (replacement.chi != null) { + // @ts-ignore for (const node of replacement.chi) { node[PARENT] = replacement; parents.add(node); @@ -115,7 +121,9 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent, co (feature.accept != null && !feature.accept.has(parent.typ))) { continue; } - const result = feature.run(replacement, options2, parent[PARENT] ?? ast, context, FeatureWalkMode.Post); + const result = feature.run(replacement, options2, + // @ts-ignore + parent[PARENT] ?? ast, context, FeatureWalkMode.Post); if (result != null) { replacement = result; } @@ -128,7 +136,9 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent, co // @ts-ignore replaceNodeOrValue(parent[PARENT], parent, replacement); } + // @ts-ignore if (replacement.chi != null) { + // @ts-ignore for (const node of replacement.chi) { node[PARENT] = replacement; parents.add(node); @@ -159,6 +169,7 @@ function transformAtRuleMediaPrelude(values) { values[values.indexOf(value)] = value.l; } else { + // @ts-ignore replaceNodeOrValue(parent, value, value.l); // @ts-ignore value = value.l; @@ -215,9 +226,11 @@ function transformAtRuleMediaPrelude(values) { // @ts-expect-error const p = parents?.[parents?.indexOf?.(parent) + 1]; if (p != null) { + // @ts-ignore replaceNodeOrValue(p, parent, replacement); } else { + // @ts-ignore values.splice(values.indexOf(parent), 1, replacement); } hasUpdates = true; @@ -331,6 +344,7 @@ function doMinify(ast, options = {}, recursive = false, errors, nestingContent, continue; } while (previous?.typ === EnumToken.CommentNodeType) { + // @ts-ignore previous = ast.chi[--nodeIndex]; } node = ast.chi[i]; @@ -353,6 +367,7 @@ function doMinify(ast, options = {}, recursive = false, errors, nestingContent, // do not merge keyframes // https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@keyframes#resolving_duplicates previous.chi.push(...node.chi); + // @ts-ignore ast.chi.splice(i, 1); previous = ast?.chi?.[nodeIndex] ?? null; i = nodeIndex; @@ -425,6 +440,7 @@ function doMinify(ast, options = {}, recursive = false, errors, nestingContent, else if (ast.typ === node.typ && ast.nam === node.nam && ast.val === node.val) { + // @ts-ignore replaceNodeOrValue(ast, node, node.chi); i--; continue; @@ -518,7 +534,9 @@ function doMinify(ast, options = {}, recursive = false, errors, nestingContent, ":is(" + node[OPTIMIZED].selector.reduce(reducer, []).join(",") + ")"; - const sel2 = node[OPTIMIZED].selector.reduce((acc, curr) => (acc.length > 0 ? acc + "," : "") + node[OPTIMIZED].optimized[0] + curr.join(""), ""); + const sel2 = node[OPTIMIZED].selector.reduce((acc, curr) => + // @ts-ignore + (acc.length > 0 ? acc + "," : "") + node[OPTIMIZED].optimized[0] + curr.join(""), ""); node.sel = sel1.length < sel2.length ? sel1 : sel2; node[TOKENS] = null; } @@ -598,7 +616,9 @@ function doMinify(ast, options = {}, recursive = false, errors, nestingContent, ":is(" + node[OPTIMIZED].selector.reduce(reducer, []).join(",") + ")"; - const sel2 = node[OPTIMIZED].selector.reduce((acc, curr) => (acc.length > 0 ? acc + "," : "") + node[OPTIMIZED].optimized[0] + curr.join(""), ""); + const sel2 = node[OPTIMIZED].selector.reduce((acc, curr) => + // @ts-ignore + (acc.length > 0 ? acc + "," : "") + node[OPTIMIZED].optimized[0] + curr.join(""), ""); node.sel = sel1.length < sel2.length ? sel1 : sel2; node[TOKENS] = null; } @@ -609,11 +629,14 @@ function doMinify(ast, options = {}, recursive = false, errors, nestingContent, curr.join(""), ""); node[TOKENS] = null; } + // @ts-ignore } else if (node[OPTIMIZED]?.optimized.length > 0) { + // @ts-ignore const sel = node[OPTIMIZED].optimized.join(""); if (sel.length < node.sel.length) { node.sel = sel; + // @ts-ignore node[RAW] = [node[OPTIMIZED].optimized.slice()]; node[TOKENS] = null; } @@ -638,9 +661,12 @@ function doMinify(ast, options = {}, recursive = false, errors, nestingContent, if (((node.typ === EnumToken.RuleNodeType || node.typ === EnumToken.KeyframesRuleNodeType) && node.sel === previous.sel) || + // @ts-ignore (node.typ == EnumToken.AtRuleNodeType && node.nam !== "font-face" && + // @ts-ignore node.nam === previous.nam)) { + // @ts-ignore node.chi.unshift(...previous.chi); doMinify(node, options, recursive, errors, nestingContent, context); ast.chi.splice(nodeIndex, 1); diff --git a/dist/lib/ast/walk.js b/dist/lib/ast/walk.js index c7260b95..007b0984 100644 --- a/dist/lib/ast/walk.js +++ b/dist/lib/ast/walk.js @@ -157,13 +157,18 @@ function* walk(node, filter, reverse) { } if (includeValues) { if (node[TOKENS] != null) { + // @ts-ignore parents.splice(i, 0, ...(reverse ? node[TOKENS].toReversed() : node[TOKENS])); + // @ts-ignore } else if (Array.isArray(node.val)) { + // @ts-ignore parents.splice(i, 0, ...(reverse ? node.val.toReversed() : node.val)); } } + // @ts-ignore if (node["chi"] != null && (!isNumeric || (option & WalkerOptionEnum.IgnoreChildren) === 0)) { + // @ts-ignore parents.splice(i, 0, ...(reverse ? node.chi.toReversed() : node.chi)); for (const child of node.chi) { map.set(child, node); diff --git a/dist/lib/parser/declaration/map.js b/dist/lib/parser/declaration/map.js index d1cfc62c..1f703256 100644 --- a/dist/lib/parser/declaration/map.js +++ b/dist/lib/parser/declaration/map.js @@ -294,7 +294,9 @@ class PropertyMap { if (t.typ == EnumToken.ImportantTokenType) { isImportant = true; } - if (filtered.length == 0 && t.typ != EnumToken.WhitespaceTokenType && t.typ != EnumToken.ImportantTokenType) { + if (filtered.length == 0 && + t.typ != EnumToken.WhitespaceTokenType && + t.typ != EnumToken.ImportantTokenType) { filtered.push(dec); } } diff --git a/dist/lib/parser/parse.js b/dist/lib/parser/parse.js index ac88964e..2ddee828 100644 --- a/dist/lib/parser/parse.js +++ b/dist/lib/parser/parse.js @@ -637,7 +637,9 @@ function doParseSync(iter, options = {}) { break; } } + // @ts-ignore if (nodes[i].chi != null) { + // @ts-ignore subNodes.push(...nodes[i].chi); } if (subNodes.length > 0) { @@ -756,6 +758,7 @@ function doParseSync(iter, options = {}) { } } if (node != nodes[i]) { + // @ts-ignore replaceNodeOrValue(nodes[i][PARENT], nodes[i], node); } } @@ -875,6 +878,7 @@ function doParseSync(iter, options = {}) { throw new Error("css variable import not supported by parseSync() or transformSync(). use parse() or transform() instead.\nat " + options.source.getSourceLocation(node[LOC].sta).join(":")); } + // @ts-ignore if (node.typ == EnumToken.CssVariableDeclarationMapTokenType) { const from = node.from.find((t) => t.typ == EnumToken.IdenTokenType || isIdentColor(t)); if (!(from.val in cssVariablesMap)) { @@ -1149,6 +1153,7 @@ function doParseSync(iter, options = {}) { } else if ((value.typ == EnumToken.IdenTokenType || isIdentColor(value)) && value.val in importedCssVariables) { + // @ts-ignore replaceNodeOrValue(parent, value, importedCssVariables[value.val].val); } } @@ -1224,7 +1229,7 @@ function doParseSync(iter, options = {}) { } if (moduleSettings.scoped & ModuleScopeEnumOptions.Pure) { if (!hasIdOrClass) { - throw new Error(`pure module: No id or class found in selector '${node.sel}' at '${node[LOC]?.src ?? ""}':${node[LOC]?.sta?.lin ?? ""}:${node[LOC]?.sta?.col ?? ""}`); + throw new Error(`pure module: No id or class found in selector '${node.sel}' at '${options.source.getOffsets(node[LOC]?.sta).join(":")}'`); } } node.sel = ""; @@ -1510,6 +1515,7 @@ async function doParse(iter, options = {}) { stats.nodesCount += root.stats.nodesCount; stats.tokensCount += root.stats.tokensCount; stats.imports.push(root.stats); + // @ts-ignore node[PARENT].chi.splice(node[PARENT].chi.indexOf(node), 1, ...root.ast.chi); if (root.errors.length > 0) { errors.push(...root.errors); @@ -1556,7 +1562,9 @@ async function doParse(iter, options = {}) { break; } } + // @ts-ignore if (nodes[i].chi != null) { + // @ts-ignore subNodes.push(...nodes[i].chi); } if (subNodes.length > 0) { @@ -1678,6 +1686,7 @@ async function doParse(iter, options = {}) { } } if (node != nodes[i]) { + // @ts-ignore replaceNodeOrValue(nodes[i][PARENT], nodes[i], node); } } @@ -1822,6 +1831,7 @@ async function doParse(iter, options = {}) { parent.chi.splice(parent.chi.indexOf(node), 1); continue; } + // @ts-ignore if (node.typ == EnumToken.CssVariableDeclarationMapTokenType) { const from = node.from.find((t) => t.typ == EnumToken.IdenTokenType || isIdentColor(t)); if (!(from.val in cssVariablesMap)) { @@ -1866,6 +1876,7 @@ async function doParse(iter, options = {}) { let result = moduleSettings.scoped & ModuleScopeEnumOptions.Global ? node.nam : moduleSettings.generateScopedName(node.nam, moduleSettings.filePath, moduleSettings.pattern, moduleSettings.hashLength); + // @ts-ignore let value = result instanceof Promise ? await result : result; mapping[node.nam] = "--" + @@ -2173,30 +2184,13 @@ async function doParse(iter, options = {}) { } for (const { value, parent } of walkValues(node.val, node)) { if (value.typ == EnumToken.DashedIdenTokenType) { - // if (!((value as DashedIdentToken).val in mapping)) { - // const result = - // moduleSettings.scoped! & ModuleScopeEnumOptions.Global - // ? (value as DashedIdentToken).val - // : moduleSettings.generateScopedName!( - // (value as DashedIdentToken).val, - // moduleSettings.filePath as string, - // moduleSettings.pattern as string, - // moduleSettings.hashLength, - // ); - // let val: string = result instanceof Promise ? await result : result; - // mapping[(value as DashedIdentToken).val] = - // "--" + - // (moduleSettings.naming! & ModuleCaseTransformEnum.DashCaseOnly || - // moduleSettings.naming! & ModuleCaseTransformEnum.CamelCaseOnly - // ? getKeyName(val, moduleSettings.naming as ModuleCaseTransformEnum) - // : val); - // revMapping[mapping[(value as DashedIdentToken).val]] = (value as DashedIdentToken).val; - // } value.val = mapping[value.val]; } else if ((value.typ == EnumToken.IdenTokenType || isIdentColor(value)) && value.val in importedCssVariables) { - replaceNodeOrValue(parent, value, importedCssVariables[value.val].val); + replaceNodeOrValue( + // @ts-ignore + parent, value, importedCssVariables[value.val].val); } } } @@ -2274,7 +2268,7 @@ async function doParse(iter, options = {}) { } if (moduleSettings.scoped & ModuleScopeEnumOptions.Pure) { if (!hasIdOrClass) { - throw new Error(`pure module: No id or class found in selector '${node.sel}' at '${node[LOC]?.src ?? ""}':${node[LOC]?.sta?.lin ?? ""}:${node[LOC]?.sta?.col ?? ""}`); + throw new Error(`pure module: No id or class found in selector '${node.sel}' at '${(options.source?.getOffsets?.(node[LOC]?.sta) ?? []).join(":")}'`); } } node.sel = ""; @@ -2894,10 +2888,13 @@ function parseAtRule(stream, context, options, errors, parseAsBlock = null) { let definedAfterLastElse = false; if (sibling == null || sibling.typ !== EnumToken.AtRuleNodeType) { missingWhen = true; + // @ts-expect-error } else if (sibling.nam !== "when") { + // @ts-expect-error if (sibling.nam !== "else") { missingWhen = true; + // @ts-expect-error } else if (sibling.val === "") { definedAfterLastElse = true; diff --git a/dist/lib/parser/tokenize.js b/dist/lib/parser/tokenize.js index e5b937aa..cadfb3bb 100644 --- a/dist/lib/parser/tokenize.js +++ b/dist/lib/parser/tokenize.js @@ -1,6 +1,6 @@ import { EnumToken, ColorType } from '../ast/types.js'; import { LOC, wildCardFuncs, whenElseFunc, transformFunctions, mathFuncs, colorsFunc, timingFunc, supportFunc, timelineFunc, imageFunc, gridTemplateFunc, urlFunc, containerFunc, pseudoElements } from '../syntax/constants.js'; -import { isDigit, isPseudo, isIdent, isWhiteSpace, isURLToken, isNumber, isHexColor, isHash, isPercentage, parseDimension, isNewLine } from '../syntax/syntax.js'; +import { isDigit, isWhiteSpace, isIdent, isHexColor, isHash, isNumber, isPercentage, parseDimension, isNewLine, isIdentStart, isIdentCodepoint, isNonPrintable } from '../syntax/syntax.js'; import { SourceFile } from './source.js'; import { equalsIgnoreCase } from './utils/text.js'; @@ -136,17 +136,17 @@ var TokenMap; TokenMap[TokenMap["GREATERTHAN"] = 62] = "GREATERTHAN"; })(TokenMap || (TokenMap = {})); function consumeString(parseInfo) { - const quote = next(parseInfo); - let value; - let buffer = quote; + const quote = next(parseInfo).charCodeAt(0); + let charCode; + let decodeSegments = false; const result = []; - while ((value = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset))) { - if (value == "\\") { - if ("\\" == parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset + 1)) { - buffer += next(parseInfo, 2); + while ((charCode = parseInfo.stream.charCodeAt(parseInfo.currentPosition - parseInfo.offset)) == charCode) { + if (charCode == 92 /* TokenMap.REVERSE_SOLIDUS */) { + if (charCode == parseInfo.stream.charCodeAt(parseInfo.currentPosition - parseInfo.offset + 1)) { + next(parseInfo, 2); continue; } - const sequence = peek(parseInfo, 6); + const sequence = peek(parseInfo, 7); let escapeSequence = ""; let codepoint; let i; @@ -165,50 +165,72 @@ function consumeString(parseInfo) { break; } if (escapeSequence.trimEnd().length > 0) { - const codepoint = parseInt(escapeSequence, 16); - if (codepoint == 0 || - // leading surrogate - (0xd800 <= codepoint && codepoint <= 0xdbff) || - // trailing surrogate - (0xdc00 <= codepoint && codepoint <= 0xdfff)) { - buffer += String.fromCodePoint(0xfffd); - } - else { - buffer += String.fromCodePoint(codepoint); - } - next(parseInfo, escapeSequence.length + + // const codepoint = parseInt(escapeSequence, 16); + // TODO set decode flag ON + // if ( + // codepoint == 0 || + // // leading surrogate + // (0xd800 <= codepoint && codepoint <= 0xdbff) || + // // trailing surrogate + // (0xdc00 <= codepoint && codepoint <= 0xdfff) + // ) { + // buffer += String.fromCodePoint(0xfffd); + // } else { + // buffer += String.fromCodePoint(codepoint); + // } + const length = escapeSequence.length + 1 + (isWhiteSpace(parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset)?.charCodeAt(0)) ? 1 - : 0)); + : 0); + decodeSegments = true; + next(parseInfo, length); continue; } - buffer += next(parseInfo, 2); + next(parseInfo, 2); continue; } - if (value == quote) { - buffer += value; - result.push(yieldResult(buffer, parseInfo, - /* hasNewLine ? EnumToken.BadStringTokenType : */ EnumToken.StringTokenType)); + if (charCode == quote) { next(parseInfo); - buffer = ""; + result.push(yieldResult(parseInfo, + /* hasNewLine ? EnumToken.BadStringTokenType : */ EnumToken.StringTokenType, decodeSegments ? { decodeSegments } : null)); return result; } - if (isNewLine(value.charCodeAt(0))) { - result.push(yieldResult(buffer + next(parseInfo), parseInfo, EnumToken.BadStringTokenType)); + if (isNewLine(charCode)) { + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.BadStringTokenType)); return result; } - buffer += value; next(parseInfo); } // EOF - 'Unclosed-string' fixed - result.push(yieldResult(buffer + quote, parseInfo, EnumToken.StringTokenType)); + result.push(yieldResult(parseInfo, EnumToken.StringTokenType)); return result; } -function yieldResult(val, parseInfo, hint) { +function yieldResult(parseInfo, hint, options) { + let val = parseInfo.stream.slice(parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset); let token = null; let dimension; - // console.debug(`Yield result: ${val}, ${hint}`); + // if (val === "" && hint != EnumToken.EOFTokenType) { + // console.error(parseInfo.stream.length > parseInfo.currentPosition - parseInfo.offset, parseInfo.position < parseInfo.currentPosition, parseInfo.currentPosition - parseInfo.offset, parseInfo.stream.slice(parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset), parseInfo.stream.length, parseInfo.currentPosition- parseInfo.offset, parseInfo.position- parseInfo.offset, parseInfo.position, parseInfo.currentPosition, parseInfo.offset) + // console.error(new Error(`val is empty '${hint}'`)); + // } + // console.error({val, hint, position: parseInfo.position - parseInfo.offset, currentPosition: parseInfo.currentPosition - parseInfo.offset, endPosition: parseInfo.stream.length, offset: parseInfo.offset, len: parseInfo.stream.length}); + // console.error(new Error('incomplete token')); + if (options?.decodeSegments) { + val = val.replace(/\\([0-9a-fA-F]{1,6})(?:\s)?/g, (_, sequence) => { + const codepoint = parseInt(sequence, 16); + if (codepoint == 0 || + // leading surrogate + (0xd800 <= codepoint && codepoint <= 0xdbff) || + // trailing surrogate + (0xdc00 <= codepoint && codepoint <= 0xdfff) || + codepoint > 0x10ffff) { + return "\uFFFD"; + } + return String.fromCodePoint(codepoint); + }); + } if (hint != null) { let searchArray = null; switch (hint) { @@ -377,6 +399,127 @@ function next(parseInfo, count = 1) { parseInfo.currentPosition += char.length; return char; } +function isIdentToken(parseInfo, start, end) { + let j = parseInfo.currentPosition - parseInfo.offset; + let i = parseInfo.position - parseInfo.offset; + if (start != null) { + if (end == null) { + if (start < 0) { + j += start; + } + else { + i += start; + } + } + else { + if (end < 0) { + j += end; + } + else { + j = parseInfo.position + end; + } + } + } + j--; + let codepoint = parseInfo.stream.charCodeAt(i); + // - + if (codepoint == 0x2d) { + let nextCodepoint; + if ((nextCodepoint = parseInfo.stream.charCodeAt(i + 1)) != nextCodepoint) { + return false; + } + // if (nextCodepoint == REVERSE_SOLIDUS) { + // return name.length > 2 && !isNewLine(name.charCodeAt(2) as number); + // } + if (isDigit(nextCodepoint)) { + return false; + } + codepoint = nextCodepoint; + i++; + } + if (codepoint !== 0x2d && !isIdentStart(codepoint)) { + return false; + } + if (codepoint == 92 /* TokenMap.REVERSE_SOLIDUS */) { + codepoint = parseInfo.stream.charCodeAt(i + 1); + // if (!isIdentCodepoint(codepoint)) { + // return false; + // } + i += String.fromCodePoint(codepoint).length; + // if (i < j) { + // codepoint = name.charCodeAt(i) as number; + // if (!isIdentCodepoint(codepoint)) { + // return false; + // } + // } + } + while (i < j) { + i += codepoint < 0x80 ? 1 : String.fromCodePoint(codepoint).length; + codepoint = parseInfo.stream.charCodeAt(i); + if (codepoint == 92 /* TokenMap.REVERSE_SOLIDUS */) { + i += codepoint < 0x80 ? 1 : String.fromCodePoint(codepoint).length; + codepoint = parseInfo.stream.charCodeAt(i); + i += codepoint < 0x80 ? 1 : String.fromCodePoint(codepoint).length; + continue; + } + if (codepoint !== 0x2d && !isIdentCodepoint(codepoint)) { + return false; + } + } + return true; +} +function isPseudo(parseInfo) { + let position = parseInfo.currentPosition - parseInfo.offset; + let endPosition = parseInfo.currentPosition - parseInfo.offset; + return (parseInfo.stream.charAt(position) == ":" && + parseInfo.stream.charAt(endPosition - 1) == "(" && + (parseInfo.stream.charAt(position + 1) == ":" + ? isIdentToken(parseInfo, 2, -1) + : isIdentToken(parseInfo, 1, -1))) || + parseInfo.stream.charAt(position + 1) == ":" + ? isIdentToken(parseInfo, 2) + : isIdentToken(parseInfo, 1); +} +function startsWith(parseInfo, input) { + let i = 0; + let j = input.length; + while (i < j) { + if (parseInfo.stream.charAt(parseInfo.position - parseInfo.offset + i) != input.charAt(i)) { + return false; + } + i++; + } + return true; +} +function isURLToken(parseInfo) { + let i = parseInfo.position - parseInfo.offset; + let c; + while (++i < parseInfo.currentPosition) { + c = parseInfo.stream.charCodeAt(i); + // single quote or double quote or start parenthesis or close parenthesis + if (isNonPrintable(c) || c == 0x27 || c == 0x22 || c == 0x28 || c == 0x29) { + return false; + } + // valid escape + if (c == 92 /* TokenMap.REVERSE_SOLIDUS */) { + i++; + if (i >= parseInfo.currentPosition) { + return false; + } + c = parseInfo.stream.charCodeAt(i); + // c is not '\n' or '\r' or '\f' + if (c == 0x6e || c == 0x72 || c == 0x66) { + return false; + } + continue; + } + // is white space + if (c == 0x20 || c == 0x09) { + break; + } + } + return i == parseInfo.currentPosition; +} /** * Tokenize CSS string * @param parseInfo @@ -385,7 +528,6 @@ function next(parseInfo, count = 1) { function tokenize(parseInfo, yieldEOFToken = true) { if (typeof parseInfo == "string") { parseInfo = { - buffer: "", stream: parseInfo, source: new SourceFile(parseInfo, [], ""), offset: 0, @@ -394,160 +536,160 @@ function tokenize(parseInfo, yieldEOFToken = true) { currentPosition: 0, }; } - let value; - let buffer = parseInfo.buffer; let charCode; let nextCharCode; const startTime = performance.now(); const result = []; // allow 10 characters buffer for the streaming parser to avoid incomplete tokens - const endPosition = parseInfo.stream.length - 10; - parseInfo.buffer = ""; - while ((value = peek(parseInfo))) { - charCode = value.charCodeAt(0); + const endPosition = parseInfo.stream.length - 1; + // NaN is not equal to NaN + while ((charCode = peek(parseInfo).charCodeAt(0)) == charCode) { switch (charCode) { case 61 /* TokenMap.EQUALS */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, EnumToken.DelimTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.DelimTokenType)); break; // '+' or '-' case 43 /* TokenMap.PLUS */: case 45 /* TokenMap.MINUS */: - next(parseInfo); - if (charCode === 43 /* TokenMap.PLUS */ && !isNumber(peek(parseInfo))) { - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + nextCharCode = peek(parseInfo).charCodeAt(0); + // not a number + if (charCode === 43 /* TokenMap.PLUS */ && !(nextCharCode >= 0x30 && nextCharCode <= 0x39)) { + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(value, parseInfo, SymbolsMapTokens[value])); + next(parseInfo); + result.push(yieldResult(parseInfo, SymbolsMapTokens[parseInfo.stream + .slice(parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset) + .toLowerCase()])); break; } - buffer += value; + next(parseInfo); break; // '{' case 123 /* TokenMap.LEFT_BRACE */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, EnumToken.BlockStartTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.BlockStartTokenType)); break; // '}' case 125 /* TokenMap.RIGHT_BRACE */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, EnumToken.BlockEndTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.BlockEndTokenType)); break; // '(' case 40 /* TokenMap.LEFT_PARENTHESIS */: - if (buffer.length > 0) { - if (buffer[0] === ":" && isPseudo(buffer)) { + if (parseInfo.position < parseInfo.currentPosition) { + if (parseInfo.stream[parseInfo.position - parseInfo.offset] === ":" && isPseudo(parseInfo)) { next(parseInfo); - result.push(yieldResult(buffer, parseInfo, EnumToken.PseudoClassFunctionTokenDefType)); - buffer = ""; + result.push(yieldResult(parseInfo, EnumToken.PseudoClassFunctionTokenDefType)); break; } - else if (isIdent(buffer)) { - const hint = buffer.startsWith("--") + else if (isIdentToken(parseInfo)) { + const hint = startsWith(parseInfo, "--") ? EnumToken.CustomFunctionTokenDefType - : (SymbolsMapTokens[buffer.toLowerCase() + "("] ?? EnumToken.FunctionTokenDefType); - result.push(yieldResult(buffer, parseInfo, hint)); + : (SymbolsMapTokens[parseInfo.stream + .slice(parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset) + .toLowerCase() + "("] ?? EnumToken.FunctionTokenDefType); + result.push(yieldResult(parseInfo, hint)); next(parseInfo); - buffer = ""; + // consume '(' + parseInfo.position = parseInfo.currentPosition; if (hint === EnumToken.UrlFunctionTokenDefType) { - buffer = ""; - value = peek(parseInfo); // consume an while (isWhiteSpace(peek(parseInfo).charCodeAt(0))) { - // buffer += next(parseInfo); next(parseInfo); - // charCode = value.charCodeAt(0); } - value = peek(parseInfo); + charCode = peek(parseInfo).charCodeAt(0); let values = null; - if (value == '"' || value == "'") { + if (charCode == 34 /* TokenMap.DOUBLE_QUOTE */ || charCode == 39 /* TokenMap.SINGLE_QUOTE */) { values = consumeString(parseInfo); } else { do { - buffer += next(parseInfo); - value = peek(parseInfo); - charCode = value.charCodeAt(0); + next(parseInfo); + // value = peek(parseInfo); + charCode = peek(parseInfo).charCodeAt(0); } while ( // !(value === "/" && match(parseInfo, "/*") && - value !== ")" && - value !== ""); + charCode !== 41 /* TokenMap.RIGHT_PARENTHESIS */ && + parseInfo.currentPosition < endPosition); } if (values != null) { - if (peek(parseInfo) === "") { + // NaN is not equal to NaN + if ((charCode = peek(parseInfo).charCodeAt(0)) != charCode) { for (let i = 0; i < values.length; i++) { values[i].token.typ = EnumToken.BadUrlTokenType; } } result.push(...values); } - else if (buffer.length > 0) { - result.push(yieldResult(buffer.trimEnd(), parseInfo, - // buffer.length > 0 - peek(parseInfo) === "" || !isURLToken(buffer) + else if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo, + // parseInfo.position < parseInfo.currentPosition + (charCode = peek(parseInfo).charCodeAt(0)) != charCode || !isURLToken(parseInfo) ? EnumToken.BadUrlTokenType : EnumToken.UrlTokenTokenType)); - buffer = ""; } } break; } } - result.push(yieldResult(next(parseInfo), parseInfo, EnumToken.StartParensTokenType)); - buffer = ""; + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.StartParensTokenType)); break; // ')' case 41 /* TokenMap.RIGHT_PARENTHESIS */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, EnumToken.EndParensTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.EndParensTokenType)); break; // '[' case 91 /* TokenMap.LEFT_BRACKETS */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, EnumToken.AttrStartTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.AttrStartTokenType)); break; // ']' case 93 /* TokenMap.RIGHT_BRACKETS */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, EnumToken.AttrEndTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.AttrEndTokenType)); break; case 59 /* TokenMap.SEMICOLON */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + // console.error(parseInfo.stream.length > parseInfo.currentPosition - parseInfo.offset, parseInfo.position < parseInfo.currentPosition, parseInfo.currentPosition - parseInfo.offset, endPosition, parseInfo.stream.slice(parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset), parseInfo.stream.length) + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, EnumToken.SemiColonTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.SemiColonTokenType)); break; case 58 /* TokenMap.COLON */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + // console.error(parseInfo.currentPosition - parseInfo.offset, parseInfo.position - parseInfo.offset, parseInfo.stream.length, parseInfo.stream.slice(parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset)); + result.push(yieldResult(parseInfo)); } next(parseInfo); if (peek(parseInfo).charCodeAt(0) == 58 /* TokenMap.COLON */) { - result.push(yieldResult(value + next(parseInfo), parseInfo, EnumToken.DoubleColonTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.DoubleColonTokenType)); break; } - result.push(yieldResult(value, parseInfo, EnumToken.ColonTokenType)); + result.push(yieldResult(parseInfo, EnumToken.ColonTokenType)); break; // \n \r \f \v \t space case 0x9: @@ -558,205 +700,203 @@ function tokenize(parseInfo, yieldEOFToken = true) { case 0xd: case 0x2028: case 0x2029: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - buffer += next(parseInfo); + next(parseInfo); nextCharCode = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset).charCodeAt(0); while (nextCharCode == 0x20 || (nextCharCode >= 0x9 && nextCharCode <= 0xd) || nextCharCode == 0x2028 || nextCharCode == 0x2029) { - value += next(parseInfo); + next(parseInfo); nextCharCode = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset).charCodeAt(0); } - result.push(yieldResult(value, parseInfo, EnumToken.WhitespaceTokenType)); - buffer = ""; + result.push(yieldResult(parseInfo, EnumToken.WhitespaceTokenType)); break; case 44 /* TokenMap.COMMA */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, EnumToken.CommaTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.CommaTokenType)); break; case 36 /* TokenMap.DOLLAR */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } if (match(parseInfo, "$=")) { - result.push(yieldResult(next(parseInfo, 2), parseInfo, EnumToken.EndMatchTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, EnumToken.EndMatchTokenType)); break; } - buffer += next(parseInfo); + next(parseInfo); break; case 126 /* TokenMap.TILDA */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } if (match(parseInfo, "~=")) { - result.push(yieldResult(next(parseInfo, 2), parseInfo, EnumToken.IncludeMatchTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, EnumToken.IncludeMatchTokenType)); break; } - result.push(yieldResult(next(parseInfo), parseInfo, EnumToken.Tilda)); - buffer = ""; + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.Tilda)); break; // case '^': case 94 /* TokenMap.CARET */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } if (match(parseInfo, "^=")) { - result.push(yieldResult(next(parseInfo, 2), parseInfo, EnumToken.StartMatchTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, EnumToken.StartMatchTokenType)); break; } - buffer += next(parseInfo); + next(parseInfo); break; case 42 /* TokenMap.STAR */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } if (match(parseInfo, "*=")) { - result.push(yieldResult(next(parseInfo, 2), parseInfo, EnumToken.ContainMatchTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, EnumToken.ContainMatchTokenType)); break; } - result.push(yieldResult(next(parseInfo), parseInfo, EnumToken.Star)); - buffer = ""; + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.Star)); break; case 38 /* TokenMap.AMPERSAND */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, EnumToken.NestingSelectorTokenType)); - buffer = ""; + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.NestingSelectorTokenType)); break; case 124 /* TokenMap.PIPE */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } // '||' if (match(parseInfo, "||")) { - result.push(yieldResult(next(parseInfo, 2), parseInfo, EnumToken.ColumnCombinatorTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, EnumToken.ColumnCombinatorTokenType)); break; } else if (match(parseInfo, "|=")) { - result.push(yieldResult(next(parseInfo, 2), parseInfo, EnumToken.DashMatchTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, EnumToken.DashMatchTokenType)); break; } - result.push(yieldResult(next(parseInfo), parseInfo, EnumToken.Pipe)); - buffer = ""; + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.Pipe)); break; case 33 /* TokenMap.EXCLAMATION */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } if (match(parseInfo, "!important")) { - result.push(yieldResult(next(parseInfo, 10), parseInfo, EnumToken.ImportantTokenType)); - buffer = ""; + next(parseInfo, 10); + result.push(yieldResult(parseInfo, EnumToken.ImportantTokenType)); break; } - buffer += next(parseInfo); + next(parseInfo); break; case 47 /* TokenMap.SLASH */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } if (!match(parseInfo, "/*")) { - result.push(yieldResult(next(parseInfo), parseInfo, SymbolsMapTokens[value])); + next(parseInfo); + result.push(yieldResult(parseInfo, SymbolsMapTokens[parseInfo.stream.slice(parseInfo.position, parseInfo.currentPosition)])); break; } - buffer += next(parseInfo, 2); - while ((value = next(parseInfo))) { - if (value == "*") { - buffer += value; + next(parseInfo, 2); + while ((charCode = next(parseInfo).charCodeAt(0)) == charCode) { + if (charCode == 42 /* TokenMap.STAR */) { if (match(parseInfo, "/")) { - result.push(yieldResult(buffer + next(parseInfo), parseInfo, EnumToken.CommentTokenType)); - buffer = ""; + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.CommentTokenType)); break; } } - else { - buffer += value; - } + // else { + // buffer += value; + // } } - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo, EnumToken.BadCommentTokenType)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo, EnumToken.BadCommentTokenType)); } break; case 62 /* TokenMap.GREATERTHAN */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } if (match(parseInfo, ">=")) { - result.push(yieldResult(next(parseInfo, 2), parseInfo, EnumToken.GteTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, EnumToken.GteTokenType)); break; } - result.push(yieldResult(next(parseInfo), parseInfo, EnumToken.GtTokenType)); - buffer = ""; + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.GtTokenType)); break; case 60 /* TokenMap.LOWERTHAN */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } if (match(parseInfo, "<=")) { - result.push(yieldResult(next(parseInfo, 2), parseInfo, EnumToken.LteTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, EnumToken.LteTokenType)); break; } - buffer += next(parseInfo); + next(parseInfo); if (match(parseInfo, "!--")) { - buffer += next(parseInfo, 3); - while ((value = next(parseInfo))) { - buffer += value; - if (value == "-" && match(parseInfo, "->")) { + next(parseInfo, 3); + while ((charCode = next(parseInfo).charCodeAt(0)) == charCode) { + if (charCode == 45 /* TokenMap.MINUS */ && match(parseInfo, "->")) { break; } } - if (value === "") { - result.push(yieldResult(buffer, parseInfo, EnumToken.BadCdoTokenType)); + if (parseInfo.currentPosition >= endPosition) { + result.push(yieldResult(parseInfo, EnumToken.BadCdoTokenType)); } else { - result.push(yieldResult(buffer + next(parseInfo, 2), parseInfo, EnumToken.CDOCOMMTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, EnumToken.CDOCOMMTokenType)); } - buffer = ""; } break; case 35 /* TokenMap.HASH */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - buffer += next(parseInfo); + next(parseInfo); break; case 92 /* TokenMap.REVERSE_SOLIDUS */: + if (!yieldEOFToken && parseInfo.stream.length == parseInfo.currentPosition - parseInfo.offset + 1) { + break; + } + // console.error('reverse solidus', parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset, parseInfo.stream.length, parseInfo.stream.slice(parseInfo.position, parseInfo.currentPosition)); next(parseInfo); // EOF if (!peek(parseInfo)) { + if (!yieldEOFToken) { + break; + } // end of stream ignore \\ - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } break; } - buffer += value + next(parseInfo); + next(parseInfo); break; case 39 /* TokenMap.SINGLE_QUOTE */: case 34 /* TokenMap.DOUBLE_QUOTE */: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } result.push(...consumeString(parseInfo)); break; @@ -764,30 +904,31 @@ function tokenize(parseInfo, yieldEOFToken = true) { const codepoint = parseInfo.stream .charAt(parseInfo.currentPosition - parseInfo.offset + 1) .charCodeAt(0); - if (!isDigit(codepoint) && buffer !== "") { - result.push(yieldResult(buffer, parseInfo)); - buffer = next(parseInfo, 2); + if (!isDigit(codepoint) && parseInfo.position !== parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); + next(parseInfo, 2); break; } - buffer += next(parseInfo); + next(parseInfo); break; default: - buffer += next(parseInfo); + next(parseInfo); break; } - if (!yieldEOFToken && endPosition <= parseInfo.stream.length - parseInfo.currentPosition + parseInfo.offset) { + if (!yieldEOFToken && endPosition <= parseInfo.currentPosition - parseInfo.offset + 1) { break; } } if (yieldEOFToken) { - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); + // console.error(parseInfo.stream.length > parseInfo.currentPosition - parseInfo.offset, parseInfo.position < parseInfo.currentPosition, parseInfo.currentPosition - parseInfo.offset, parseInfo.position - parseInfo.offset, parseInfo.stream.length); + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult("", parseInfo, EnumToken.EOFTokenType)); - } - else { - parseInfo.buffer = buffer; + result.push(yieldResult(parseInfo, EnumToken.EOFTokenType)); } + // else { + // // parseInfo.buffer = buffer; + // } parseInfo.time += performance.now() - startTime; return result; } @@ -799,19 +940,17 @@ function tokenize(parseInfo, yieldEOFToken = true) { async function* tokenizeStream(input, parseInfo) { const decoder = new TextDecoder("utf-8"); const reader = input.getReader(); + parseInfo.stream = ""; while (true) { const { done, value } = await reader.read(); const stream = ArrayBuffer.isView(value) ? decoder.decode(value, { stream: true }) : value; if (!done) { parseInfo.source.append(stream); - if (typeof parseInfo.stream != "string") { - parseInfo.stream = stream; - } - else { - parseInfo.stream = (parseInfo.stream.slice(parseInfo.currentPosition - parseInfo.offset) + - stream); - } - parseInfo.offset = parseInfo.currentPosition; + parseInfo.stream = (parseInfo.stream.slice(parseInfo.position - parseInfo.offset) + stream); + parseInfo.offset = parseInfo.offset = parseInfo.position; + } + else { + parseInfo.stream = ""; } yield* tokenize(parseInfo, done); if (done) { diff --git a/dist/lib/parser/utils/at-rule-container.js b/dist/lib/parser/utils/at-rule-container.js index ed6c5c06..3fab097b 100644 --- a/dist/lib/parser/utils/at-rule-container.js +++ b/dist/lib/parser/utils/at-rule-container.js @@ -138,7 +138,8 @@ function parseAtRuleContainerQueryList(stream, context, options = {}) { success = false; errors.push({ action: "drop", - node: options.source.getSourceLocation(stream[i][LOC].sta), + node: stream[i], + location: options.source.getSourceLocation(stream[i][LOC].sta), message: ` is not allowed outside of parentheses`, }); break; diff --git a/dist/lib/parser/utils/selector.js b/dist/lib/parser/utils/selector.js index 4c4d0b80..d3650aac 100644 --- a/dist/lib/parser/utils/selector.js +++ b/dist/lib/parser/utils/selector.js @@ -7,6 +7,7 @@ import { matchAllSyntaxes, createValidationContext, trimArray, matchSelectorSynt import { ValidationSyntaxGroupEnum, ValidationTokenEnum } from '../../validation/parser/typedef.js'; import { splitTokenList } from '../../validation/utils/list.js'; import { trimWhiteSpace } from '../parse.js'; +import { equalsIgnoreCase } from './text.js'; /** * parse selector @@ -266,6 +267,29 @@ function parseSelector(tokens, context, options, errors) { func.val == ":nth-last-child" || func.val == ":nth-of-type" || func.val == ":nth-last-of-type") { + const list = []; + let index; + for (index = 0; index < func.chi.length; index++) { + if (func.chi[index].typ == EnumToken.CommentTokenType || func.chi[index].typ == EnumToken.WhitespaceTokenType) { + continue; + } + if (func.chi[index].typ == EnumToken.IdenTokenType && equalsIgnoreCase('of', func.chi[index].val)) { + index--; + break; + } + list.push(func.chi[index]); + } + if (list.length == 3) { + if (list[0].typ == EnumToken.IdenTokenType && ('n' == list[0].val || '-n' == list[0].val || '+n' == list[0].val)) { + if (list[1].typ == EnumToken.NextSiblingCombinatorTokenType) { + if (list[2].typ == EnumToken.NumberTokenType && (0 == list[2].val)) { + list[0].val = 'n'; + func.chi.splice(0, index, list[0]); + break; + } + } + } + } const token = func.chi.find((t) => t.typ != EnumToken.WhitespaceTokenType && t.typ != EnumToken.CommentTokenType); if (token?.typ == EnumToken.IdenTokenType || token?.typ == EnumToken.LiteralTokenType) { if (token.typ == EnumToken.IdenTokenType && diff --git a/dist/lib/renderer/sourcemap/sourcemap.js b/dist/lib/renderer/sourcemap/sourcemap.js index 1c547f17..ff28f482 100644 --- a/dist/lib/renderer/sourcemap/sourcemap.js +++ b/dist/lib/renderer/sourcemap/sourcemap.js @@ -53,7 +53,6 @@ class SourceMap { /** * * @param sourcemaps - * @private */ constructor(sourcemaps) { if (typeof sourcemaps === "string") { @@ -111,7 +110,6 @@ class SourceMap { * Add all location * @param maps * @throws - * @private */ add(...maps) { let srcIndex; @@ -159,6 +157,7 @@ class SourceMap { let sourceFileIndex = 0; // second field let sourceCodeLine = 0; // third field let sourceCodeColumn = 0; // fourth field + // let nameIndex: number = 0; // fifth field let generatedCodeColumn; let result; // mappings to original source diff --git a/dist/lib/syntax/syntax.js b/dist/lib/syntax/syntax.js index 6e83b1d0..aed77b24 100644 --- a/dist/lib/syntax/syntax.js +++ b/dist/lib/syntax/syntax.js @@ -794,6 +794,9 @@ const isIdent = memoize(function (name) { return false; } if (codepoint == REVERSE_SOLIDUS) { + if (i + 1 > j) { + return false; + } codepoint = name.charCodeAt(i + 1); // if (!isIdentCodepoint(codepoint)) { // return false; @@ -830,35 +833,6 @@ function isNonPrintable(codepoint) { codepoint == 0x7f || (codepoint >= 0xe && codepoint <= 0x1f)); } -function isURLToken(str) { - let i = -1; - let c; - while (++i < str.length) { - c = str.charCodeAt(i); - // single quote or double quote or start parenthesis or close parenthesis - if (isNonPrintable(c) || c == 0x27 || c == 0x22 || c == 0x28 || c == 0x29) { - return false; - } - // valid escape - if (c == REVERSE_SOLIDUS) { - i++; - if (i >= str.length) { - return false; - } - c = str.charCodeAt(i); - // c is not '\n' or '\r' or '\f' - if (c == 0x6e || c == 0x72 || c == 0x66) { - return false; - } - continue; - } - // is white space - if (c == 0x20 || c == 0x09) { - break; - } - } - return i == str.length; -} function isPseudo(name) { return (name.charAt(0) == ":" && ((name.endsWith("(") && isIdent(name.charAt(1) == ":" ? name.slice(2, -1) : name.slice(1, -1))) || @@ -1114,4 +1088,4 @@ function toPrecisionAngle(angle, precision = colorPrecision, correctValue = true return angle; } -export { dimensionUnits, isAngle, isColor, isDigit, isFlex, isFrequency, isFunction, isHash, isHexColor, isIdent, isIdentCodepoint, isIdentColor, isIdentStart, isLength, isLetter, isNewLine, isNonPrintable, isNumber, isPercentage, isPolarColorspace, isPseudo, isRectangularOrthogonalColorspace, isResolution, isTime, isURLToken, isWhiteSpace, length2Px, minifyNumber, parseColor, parseDimension, pseudoAliasMap, reduceColorStops, reduceConicColorStops, reducegradientBackgroundPosition, renamedStandardProperties, toPrecisionAngle, toPrecisionValue }; +export { dimensionUnits, isAngle, isColor, isDigit, isFlex, isFrequency, isFunction, isHash, isHexColor, isIdent, isIdentCodepoint, isIdentColor, isIdentStart, isLength, isLetter, isNewLine, isNonPrintable, isNumber, isPercentage, isPolarColorspace, isPseudo, isRectangularOrthogonalColorspace, isResolution, isTime, isWhiteSpace, length2Px, minifyNumber, parseColor, parseDimension, pseudoAliasMap, reduceColorStops, reduceConicColorStops, reducegradientBackgroundPosition, renamedStandardProperties, toPrecisionAngle, toPrecisionValue }; diff --git a/dist/lib/validation/match.js b/dist/lib/validation/match.js index 23f06fbc..6ee6bc51 100644 --- a/dist/lib/validation/match.js +++ b/dist/lib/validation/match.js @@ -990,6 +990,7 @@ function matchSyntax(syntaxes, context, options) { return result; } if (tokensfuncDefMap.has(token.typ) && + // @ts-ignore token.typ === EnumToken.WildCardFunctionTokenDefType) { const range = trimArray(context.peekRange()); result = matchSyntax(getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, token.val + "()")?.[0]?.chi, createValidationContext(range.slice(1, -1)), options); @@ -1990,7 +1991,9 @@ function matchProperty(property, context, options) { // ) // ) { const newRange = range.map((t) => cloneNode(t, true)); + // @ts-ignore parseTokens(newRange, { parseColor: true }, errors); + // @ts-ignore success = newRange.length == 1 && isColor(newRange[0], errors); if (success) { context.update(range.at(-1)); diff --git a/dist/node.js b/dist/node.js index 02119cbb..4433f7b4 100644 --- a/dist/node.js +++ b/dist/node.js @@ -25,6 +25,7 @@ export { cloneNode } from './lib/ast/clone.js'; export { replaceNodeOrValue } from './lib/parser/utils/token.js'; export { SourceMap } from './lib/renderer/sourcemap/sourcemap.js'; export { FeatureWalkMode } from './lib/ast/features/type.js'; +export { getNodeProperty, setNodeProperty } from './lib/ast/node.js'; /** * Load file or url @@ -109,7 +110,7 @@ function render(data, options = {}, mapping) { return doRender(data, Object.assign(options, { resolve, dirname, cwd: options.cwd ?? resolve$1() }), mapping); } /** - * Parse css file + * Parse CSS file * @param file url or path * @param options * @param asStream load file as stream @@ -135,9 +136,8 @@ function render(data, options = {}, mapping) { */ const parseFile = deprecate(async (file, options = {}, asStream = false) => parse({ file, asStream, ...options }), "parseFile is deprecated, use parse instead as parse({file, asStream, ...options})"); /** - * Parse css + * Parse CSS * @param args - * @private * * Parsing a string * @@ -194,7 +194,7 @@ function parseSync(...args) { return !options.module && !options.inputSourceMap && !options.sourcemap ? result : parseResult(result, options); } /** - * Transform css + * Transform CSS * * ```ts * @@ -206,7 +206,6 @@ function parseSync(...args) { * ``` * * @param args - * @private */ function transformSync(...args) { let options; @@ -222,7 +221,15 @@ function transformSync(...args) { stream = input; } options ??= {}; - options = { minify: true, removeEmpty: true, removeCharset: true, ...options }; + if (options.minify == null) { + options.minify = true; + } + if (options.removeEmpty == null) { + options.removeEmpty = true; + } + if (options.removeCharset == null) { + options.removeCharset = true; + } const startTime = performance.now(); const parseResult = parseSync(stream, options); let mapping = null; @@ -255,11 +262,10 @@ function transformSync(...args) { }; } /** - * Parse css + * Parse CSS * @param args * * @throws Error file not found - * @private * * Parsing a string * @@ -374,7 +380,7 @@ const transformFile = deprecate(async (file, options = {}, asStream = false) => ...options, }), "transformFile is deprecated, use transform instead as transform({file, asStream, ...options})"); /** - * Transform css + * Transform CSS * * Parsing a string * @@ -414,7 +420,6 @@ const transformFile = deprecate(async (file, options = {}, asStream = false) => * console.log(result.code); * ``` * @param args - * @private */ async function transform(...args) { let options; @@ -435,7 +440,15 @@ async function transform(...args) { } } options ??= {}; - options = { minify: true, removeEmpty: true, removeCharset: true, ...options }; + if (options.minify == null) { + options.minify = true; + } + if (options.removeEmpty == null) { + options.removeEmpty = true; + } + if (options.removeCharset == null) { + options.removeCharset = true; + } const startTime = performance.now(); return parse(stream, options).then((parseResult) => { let mapping = null; diff --git a/dist/utils/sync.d.ts b/dist/utils/sync.d.ts index 5b81e527..64da9f8a 100644 --- a/dist/utils/sync.d.ts +++ b/dist/utils/sync.d.ts @@ -7,4 +7,10 @@ import type { ParseResult, ParserOptions, ParserSyncOptions } from "../@types/in * @private */ export declare function parseResult(result: ParseResult, options: ParserOptions): ParseResult; +/** + * + * @param options + * @param prefix + * @private + */ export declare function validateSyncArguments(options: ParserSyncOptions, prefix?: string): void; diff --git a/dist/utils/sync.js b/dist/utils/sync.js index d249c12b..04900102 100644 --- a/dist/utils/sync.js +++ b/dist/utils/sync.js @@ -27,6 +27,12 @@ function parseResult(result, options) { } return result; } +/** + * + * @param options + * @param prefix + * @private + */ function validateSyncArguments(options, prefix = "options.") { const args = Object.entries(options); let i; diff --git a/dist/web.js b/dist/web.js index ee36b442..53779329 100644 --- a/dist/web.js +++ b/dist/web.js @@ -19,6 +19,7 @@ export { cloneNode } from './lib/ast/clone.js'; export { replaceNodeOrValue } from './lib/parser/utils/token.js'; export { SourceMap } from './lib/renderer/sourcemap/sourcemap.js'; export { FeatureWalkMode } from './lib/ast/features/type.js'; +export { getNodeProperty, setNodeProperty } from './lib/ast/node.js'; /** * Load file or url @@ -99,7 +100,7 @@ function render(data, options = {}, mapping) { }), mapping); } /** - * Parse css file + * Parse CSS file * @param file url or path * @param options * @param asStream load file as stream @@ -127,9 +128,8 @@ async function parseFile(file, options = {}, asStream = false) { return parse({ file, asStream, ...options }); } /** - * Parse css + * Parse CSS * @param args - * @private * * Parsing a string * @@ -200,7 +200,6 @@ function parseSync(...args) { * ``` * * @param args - * @private */ function transformSync(...args) { let options; @@ -249,7 +248,7 @@ function transformSync(...args) { }; } /** - * Parse css + * Parse CSS * * Example: * @@ -274,7 +273,6 @@ function transformSync(...args) { * console.log(result.ast); * ``` * @param args - * @private */ async function parse(...args) { let options; @@ -353,7 +351,7 @@ async function transformFile(file, options = {}, asStream = false) { }); } /** - * Transform css + * Transform CSS * * Example: * @@ -372,7 +370,6 @@ async function transformFile(file, options = {}, asStream = false) { * console.log(result.code); * ``` * @param args - * @private */ async function transform(...args) { let options; diff --git a/src/@types/parse.d.ts b/src/@types/parse.d.ts index 6ed10243..0cee2f0b 100644 --- a/src/@types/parse.d.ts +++ b/src/@types/parse.d.ts @@ -1,7 +1,6 @@ -import {SourceFile} from "../lib/parser/source.ts"; +import { SourceFile } from "../lib/parser/source.ts"; export declare interface PropertyListOptions { - removeDuplicateDeclarations?: boolean | string | string[]; computeShorthand?: boolean; } @@ -10,11 +9,6 @@ export declare interface PropertyListOptions { * parse info */ export declare interface ParseInfo { - - /** - * read buffer - */ - buffer: string; /** * stream */ @@ -24,7 +18,7 @@ export declare interface ParseInfo { * Source file */ source: SourceFile; - + /** * last token position */ diff --git a/src/config.json b/src/config.json index efd48706..486e67fb 100644 --- a/src/config.json +++ b/src/config.json @@ -1 +1,1506 @@ -{"properties":{"gap":{"shorthand":"gap","properties":["row-gap","column-gap"],"types":["Length","Perc"],"multiple":false,"separator":null,"keywords":["normal"]},"row-gap":{"shorthand":"gap"},"column-gap":{"shorthand":"gap"},"inset":{"shorthand":"inset","properties":["top","right","bottom","left"],"types":["Length","Perc"],"multiple":false,"separator":null,"keywords":["auto"]},"top":{"shorthand":"inset"},"right":{"shorthand":"inset"},"bottom":{"shorthand":"inset"},"left":{"shorthand":"inset"},"margin":{"shorthand":"margin","properties":["margin-top","margin-right","margin-bottom","margin-left"],"types":["Length","Perc"],"multiple":false,"separator":null,"keywords":["auto"]},"margin-top":{"shorthand":"margin"},"margin-right":{"shorthand":"margin"},"margin-bottom":{"shorthand":"margin"},"margin-left":{"shorthand":"margin"},"padding":{"shorthand":"padding","properties":["padding-top","padding-right","padding-bottom","padding-left"],"types":["Length","Perc"],"keywords":[]},"padding-top":{"shorthand":"padding"},"padding-right":{"shorthand":"padding"},"padding-bottom":{"shorthand":"padding"},"padding-left":{"shorthand":"padding"},"border-radius":{"shorthand":"border-radius","properties":["border-top-left-radius","border-top-right-radius","border-bottom-right-radius","border-bottom-left-radius"],"types":["Length","Perc"],"multiple":true,"separator":{"typ":"Literal","val":"/"},"keywords":[]},"border-top-left-radius":{"shorthand":"border-radius"},"border-top-right-radius":{"shorthand":"border-radius"},"border-bottom-right-radius":{"shorthand":"border-radius"},"border-bottom-left-radius":{"shorthand":"border-radius"},"border-width":{"shorthand":"border-width","map":"border","properties":["border-top-width","border-right-width","border-bottom-width","border-left-width"],"types":["Length","Perc"],"default":["medium"],"keywords":["thin","medium","thick"]},"border-top-width":{"map":"border","shorthand":"border-width"},"border-right-width":{"map":"border","shorthand":"border-width"},"border-bottom-width":{"map":"border","shorthand":"border-width"},"border-left-width":{"map":"border","shorthand":"border-width"},"border-style":{"shorthand":"border-style","map":"border","properties":["border-top-style","border-right-style","border-bottom-style","border-left-style"],"types":[],"default":["none"],"keywords":["none","hidden","dotted","dashed","solid","double","groove","ridge","inset","outset"]},"border-top-style":{"map":"border","shorthand":"border-style"},"border-right-style":{"map":"border","shorthand":"border-style"},"border-bottom-style":{"map":"border","shorthand":"border-style"},"border-left-style":{"map":"border","shorthand":"border-style"},"border-color":{"shorthand":"border-color","map":"border","properties":["border-top-color","border-right-color","border-bottom-color","border-left-color"],"types":["Color"],"default":["currentcolor"],"keywords":[]},"border-top-color":{"map":"border","shorthand":"border-color"},"border-right-color":{"map":"border","shorthand":"border-color"},"border-bottom-color":{"map":"border","shorthand":"border-color"},"border-left-color":{"map":"border","shorthand":"border-color"},"grid-row":{"shorthand":"grid-row","properties":["grid-row-start","grid-row-end"],"types":["Iden","Number"],"multiple":true,"valueSeparator":{"typ":"Literal","val":"/"},"default":["auto"],"keywords":["auto","span"]},"grid-row-start":{"shorthand":"grid-row"},"grid-row-end":{"shorthand":"grid-row"}},"map":{"flex-flow":{"shorthand":"flex-flow","pattern":"flex-direction flex-wrap","keywords":[],"default":["row","nowrap"],"properties":{"flex-direction":{"keywords":["row","row-reverse","column","column-reverse"],"default":["row"],"types":[]},"flex-wrap":{"keywords":["wrap","nowrap","wrap-reverse"],"default":["nowrap"],"types":[]}}},"flex-direction":{"shorthand":"flex-flow"},"flex-wrap":{"shorthand":"flex-flow"},"container":{"shorthand":"container","pattern":"container-name container-type","keywords":[],"default":[],"properties":{"container-name":{"required":true,"multiple":true,"keywords":["none"],"default":["none"],"types":["Iden","DashedIden"]},"container-type":{"previous":"container-name","prefix":{"typ":"Literal","val":"/"},"keywords":["size","inline-size","normal"],"default":["normal"],"types":[]}}},"container-name":{"shorthand":"container"},"container-type":{"shorthand":"container"},"flex":{"shorthand":"flex","pattern":"flex-grow flex-shrink flex-basis","keywords":["auto","none","initial"],"default":[],"mapping":{"0 1 auto":"initial","0 0 auto":"none","1 1 auto":"auto"},"properties":{"flex-grow":{"required":true,"keywords":[],"default":[],"types":["Number"]},"flex-shrink":{"keywords":[],"default":[],"types":["Number"]},"flex-basis":{"keywords":["max-content","min-content","fit-content","fit-content","content","auto"],"default":[],"types":["Length","Perc"]}}},"flex-grow":{"shorthand":"flex"},"flex-shrink":{"shorthand":"flex"},"flex-basis":{"shorthand":"flex"},"columns":{"shorthand":"columns","pattern":"column-count column-width","keywords":["auto"],"default":["auto","auto auto"],"properties":{"column-count":{"keywords":["auto"],"default":["auto"],"types":["Number"]},"column-width":{"keywords":["auto"],"default":["auto"],"types":["Length"]}}},"column-count":{"shorthand":"columns"},"column-width":{"shorthand":"columns"},"transition":{"shorthand":"transition","multiple":true,"separator":{"typ":"Comma"},"pattern":"transition-property transition-duration transition-timing-function transition-delay transition-behavior","keywords":["none","all"],"default":["0s","0ms","all","ease","none","normal"],"mapping":{"cubic-bezier(.25,.1,.25,1)":"ease","cubic-bezier(0,0,1,1)":"linear","cubic-bezier(.42,0,1,1)":"ease-in","cubic-bezier(0,0,.58,1)":"ease-out","cubic-bezier(.42,0,.58,.42)":"ease-in-out"},"properties":{"transition-property":{"keywords":["none","all"],"default":["all"],"types":["Iden"]},"transition-duration":{"keywords":[],"default":["0s","0ms","normal"],"types":["Time"]},"transition-timing-function":{"keywords":["ease","ease-in","ease-out","ease-in-out","linear","step-start","step-end"],"default":["ease"],"types":["TimingFunction"],"mapping":{"cubic-bezier(.25,.1,.25,1)":"ease","cubic-bezier(0,0,1,1)":"linear","cubic-bezier(.42,0,1,1)":"ease-in","cubic-bezier(0,0,.58,1)":"ease-out","cubic-bezier(.42,0,.58,.42)":"ease-in-out"}},"transition-delay":{"keywords":[],"default":["0s"],"types":["Time"]},"transition-behavior":{"keywords":["normal","allow-discrete"],"default":["normal"],"types":[]}}},"transition-property":{"shorthand":"transition"},"transition-duration":{"shorthand":"transition"},"transition-timing-function":{"shorthand":"transition"},"transition-delay":{"shorthand":"transition"},"transition-behavior":{"shorthand":"transition"},"animation":{"shorthand":"animation","separator":{"typ":"Comma"},"pattern":"animation-name animation-duration animation-timing-function animation-delay animation-iteration-count animation-direction animation-fill-mode animation-play-state animation-timeline","default":["1","0s","0ms","none","ease","normal","running","auto"],"properties":{"animation-name":{"keywords":["none"],"default":["none"],"types":["Iden"]},"animation-duration":{"keywords":["auto"],"default":["0s","0ms","auto"],"types":["Time"],"mapping":{"auto":"0s"}},"animation-timing-function":{"keywords":["ease","ease-in","ease-out","ease-in-out","linear","step-start","step-end"],"default":["ease"],"types":["TimingFunction"],"mapping":{"cubic-bezier(.25,.1,.25,1)":"ease","cubic-bezier(0,0,1,1)":"linear","cubic-bezier(.42,0,1,1)":"ease-in","cubic-bezier(0,0,.58,1)":"ease-out","cubic-bezier(.42,0,.58,.42)":"ease-in-out"}},"animation-delay":{"keywords":[],"default":["0s","0ms"],"types":["Time"]},"animation-iteration-count":{"keywords":["infinite"],"default":["1"],"types":["Number"]},"animation-direction":{"keywords":["normal","reverse","alternate","alternate-reverse"],"default":["normal"],"types":[]},"animation-fill-mode":{"keywords":["none","forwards","backwards","both"],"default":["none"],"types":[]},"animation-play-state":{"keywords":["running","paused"],"default":["running"],"types":[]},"animation-timeline":{"keywords":["none","auto"],"default":["auto"],"types":["DashedIden","TimelineFunction"]}}},"animation-name":{"shorthand":"animation"},"animation-duration":{"shorthand":"animation"},"animation-timing-function":{"shorthand":"animation"},"animation-delay":{"shorthand":"animation"},"animation-iteration-count":{"shorthand":"animation"},"animation-direction":{"shorthand":"animation"},"animation-fill-mode":{"shorthand":"animation"},"animation-play-state":{"shorthand":"animation"},"animation-timeline":{"shorthand":"animation"},"text-emphasis":{"shorthand":"text-emphasis","pattern":"text-emphasis-color text-emphasis-style","default":["none","currentcolor"],"properties":{"text-emphasis-style":{"keywords":["none","filled","open","dot","circle","double-circle","triangle","sesame"],"default":["none"],"types":["String"]},"text-emphasis-color":{"default":["currentcolor"],"types":["Color"]}}},"text-emphasis-style":{"shorthand":"text-emphasis"},"text-emphasis-color":{"shorthand":"text-emphasis"},"border":{"shorthand":"border","pattern":"border-color border-style border-width","keywords":["none"],"default":["0","none"],"properties":{"border-color":{"types":["Color"],"default":["currentcolor"],"keywords":[]},"border-style":{"types":[],"default":["none"],"keywords":["none","hidden","dotted","dashed","solid","double","groove","ridge","inset","outset"]},"border-width":{"types":["Length","Perc"],"default":["medium"],"keywords":["thin","medium","thick"]}}},"border-color":{"shorthand":"border"},"border-style":{"shorthand":"border"},"border-width":{"shorthand":"border"},"list-style":{"shorthand":"list-style","pattern":"list-style-type list-style-position list-style-image","keywords":["none","outside"],"default":["none","outside"],"properties":{"list-style-position":{"types":[],"default":["outside"],"keywords":["inside","outside"]},"list-style-image":{"default":["none"],"keywords":["node"],"types":["UrlFunc","ImageFunc"]},"list-style-type":{"types":["String","Iden","Symbols"],"default":["disc"],"keywords":["disc","circle","square","decimal","decimal-leading-zero","lower-roman","upper-roman","lower-greek","lower-latin","upper-latin","none"]}}},"list-style-position":{"shorthand":"list-style"},"list-style-image":{"shorthand":"list-style"},"list-style-type":{"shorthand":"list-style"},"overflow":{"shorthand":"overflow","pattern":"overflow-x overflow-y","keywords":["auto","visible","hidden","clip","scroll"],"default":[],"mapping":{"visible visible":"visible","auto auto":"auto","hidden hidden":"hidden","scroll scroll":"scroll"},"properties":{"overflow-x":{"default":[],"types":[],"keywords":["auto","visible","hidden","clip","scroll"]},"overflow-y":{"default":[],"types":[],"keywords":["auto","visible","hidden","clip","scroll"]}}},"overflow-x":{"shorthand":"overflow"},"overflow-y":{"shorthand":"overflow"},"outline":{"shorthand":"outline","pattern":"outline-color outline-style outline-width","keywords":["none"],"default":["0","none","currentcolor"],"properties":{"outline-color":{"types":["Color"],"default":["currentcolor"],"keywords":["currentcolor"]},"outline-style":{"types":[],"default":["none"],"keywords":["auto","none","dotted","dashed","solid","double","groove","ridge","inset","outset"]},"outline-width":{"types":["Length","Perc"],"default":["medium"],"keywords":["thin","medium","thick"]}}},"outline-color":{"shorthand":"outline"},"outline-style":{"shorthand":"outline"},"outline-width":{"shorthand":"outline"},"font":{"shorthand":"font","pattern":"font-weight font-style font-size line-height font-stretch font-variant font-family","keywords":["caption","icon","menu","message-box","small-caption","status-bar","-moz-window, ","-moz-document, ","-moz-desktop, ","-moz-info, ","-moz-dialog","-moz-button","-moz-pull-down-menu","-moz-list","-moz-field"],"default":[],"properties":{"font-weight":{"types":["Number"],"default":["400","normal"],"keywords":["normal","bold","lighter","bolder"],"constraints":{"value":{"min":"1","max":"1000"}},"mapping":{"thin":"100","hairline":"100","extra light":"200","ultra light":"200","light":"300","normal":"400","regular":"400","medium":"500","semi bold":"600","demi bold":"600","bold":"700","extra bold":"800","ultra bold":"800","black":"900","heavy":"900","extra black":"950","ultra black":"950"}},"font-style":{"types":["Angle"],"default":["normal"],"keywords":["normal","italic","oblique"]},"font-size":{"types":["Length","Perc"],"default":[],"keywords":["xx-small","x-small","small","medium","large","x-large","xx-large","xxx-large","larger","smaller"],"required":true},"line-height":{"types":["Length","Perc","Number"],"default":["normal"],"keywords":["normal"],"previous":"font-size","prefix":{"typ":"Literal","val":"/"}},"font-stretch":{"types":["Perc"],"default":["normal"],"keywords":["ultra-condensed","extra-condensed","condensed","semi-condensed","normal","semi-expanded","expanded","extra-expanded","ultra-expanded"],"mapping":{"ultra-condensed":"50%","extra-condensed":"62.5%","condensed":"75%","semi-condensed":"87.5%","normal":"100%","semi-expanded":"112.5%","expanded":"125%","extra-expanded":"150%","ultra-expanded":"200%"}},"font-variant":{"types":[],"default":["normal"],"keywords":["normal","none","common-ligatures","no-common-ligatures","discretionary-ligatures","no-discretionary-ligatures","historical-ligatures","no-historical-ligatures","contextual","no-contextual","historical-forms","small-caps","all-small-caps","petite-caps","all-petite-caps","unicase","titling-caps","ordinal","slashed-zero","lining-nums","oldstyle-nums","proportional-nums","tabular-nums","diagonal-fractions","stacked-fractions","ordinal","slashed-zero","ruby","jis78","jis83","jis90","jis04","simplified","traditional","full-width","proportional-width","ruby","sub","super","text","emoji","unicode"]},"font-family":{"types":["String","Iden"],"default":[],"keywords":["serif","sans-serif","monospace","cursive","fantasy","system-ui","ui-serif","ui-sans-serif","ui-monospace","ui-rounded","math","emoji","fangsong"],"required":true,"multiple":true,"separator":{"typ":"Comma"}}}},"font-weight":{"shorthand":"font"},"font-style":{"shorthand":"font"},"font-size":{"shorthand":"font"},"line-height":{"shorthand":"font"},"font-stretch":{"shorthand":"font"},"font-variant":{"shorthand":"font"},"font-family":{"shorthand":"font"},"background":{"shorthand":"background","pattern":"background-attachment background-origin background-clip background-color background-image background-repeat background-position background-size","keywords":["none"],"default":["0 0","none","auto","repeat","transparent","#0000","scroll","padding-box","border-box"],"multiple":true,"set":{"background-origin":["background-clip"]},"separator":{"typ":"Comma"},"properties":{"background-repeat":{"types":[],"default":["repeat"],"multiple":true,"keywords":["repeat-x","repeat-y","repeat","space","round","no-repeat"],"mapping":{"repeat no-repeat":"repeat-x","no-repeat repeat":"repeat-y","repeat repeat":"repeat","space space":"space","round round":"round","no-repeat no-repeat":"no-repeat"}},"background-color":{"types":["Color"],"default":["#0000","transparent"],"multiple":true,"keywords":[]},"background-image":{"types":["UrlFunc","ImageFunc"],"default":["none"],"keywords":["none"]},"background-attachment":{"types":[],"default":["scroll"],"multiple":true,"keywords":["scroll","fixed","local"]},"background-clip":{"types":[],"default":["border-box"],"multiple":true,"keywords":["border-box","padding-box","content-box","text"]},"background-origin":{"types":[],"default":["padding-box"],"multiple":true,"keywords":["border-box","padding-box","content-box"]},"background-position":{"multiple":true,"types":["Perc","Length"],"default":["0 0","top left","left top"],"keywords":["top","left","center","bottom","right"],"mapping":{"left":"0","top":"0","center":"50%","center center":"50%","50% 50%":"50%","bottom":"100%","right":"100%"},"constraints":{"mapping":{"max":2}}},"background-size":{"multiple":true,"previous":"background-position","prefix":{"typ":"Literal","val":"/"},"types":["Perc","Length"],"default":["auto","auto auto"],"keywords":["auto","cover","contain"],"mapping":{"auto auto":"auto"}}}},"background-repeat":{"shorthand":"background"},"background-color":{"shorthand":"background"},"background-image":{"shorthand":"background"},"background-attachment":{"shorthand":"background"},"background-clip":{"shorthand":"background"},"background-origin":{"shorthand":"background"},"background-position":{"shorthand":"background"},"background-size":{"shorthand":"background"}},"property":{"transform-origin":{"pattern":[["left|center|right","top|center|bottom",""],["left|center|right","top|center|bottom"],["center|left|right|center|top|bottom|"]],"mapping":{"center":{"typ":"Perc","val":50},"left":{"typ":"Perc","val":0},"right":{"typ":"Perc","val":100},"top":{"typ":"Perc","val":0},"bottom":{"typ":"Perc","val":100}}}}} \ No newline at end of file +{ + "properties": { + "gap": { + "shorthand": "gap", + "properties": [ + "row-gap", + "column-gap" + ], + "types": [ + "Length", + "Perc" + ], + "multiple": false, + "separator": null, + "keywords": [ + "normal" + ] + }, + "row-gap": { + "shorthand": "gap" + }, + "column-gap": { + "shorthand": "gap" + }, + "inset": { + "shorthand": "inset", + "properties": [ + "top", + "right", + "bottom", + "left" + ], + "types": [ + "Length", + "Perc" + ], + "multiple": false, + "separator": null, + "keywords": [ + "auto" + ] + }, + "top": { + "shorthand": "inset" + }, + "right": { + "shorthand": "inset" + }, + "bottom": { + "shorthand": "inset" + }, + "left": { + "shorthand": "inset" + }, + "margin": { + "shorthand": "margin", + "properties": [ + "margin-top", + "margin-right", + "margin-bottom", + "margin-left" + ], + "types": [ + "Length", + "Perc" + ], + "multiple": false, + "separator": null, + "keywords": [ + "auto" + ] + }, + "margin-top": { + "shorthand": "margin" + }, + "margin-right": { + "shorthand": "margin" + }, + "margin-bottom": { + "shorthand": "margin" + }, + "margin-left": { + "shorthand": "margin" + }, + "padding": { + "shorthand": "padding", + "properties": [ + "padding-top", + "padding-right", + "padding-bottom", + "padding-left" + ], + "types": [ + "Length", + "Perc" + ], + "keywords": [] + }, + "padding-top": { + "shorthand": "padding" + }, + "padding-right": { + "shorthand": "padding" + }, + "padding-bottom": { + "shorthand": "padding" + }, + "padding-left": { + "shorthand": "padding" + }, + "border-radius": { + "shorthand": "border-radius", + "properties": [ + "border-top-left-radius", + "border-top-right-radius", + "border-bottom-right-radius", + "border-bottom-left-radius" + ], + "types": [ + "Length", + "Perc" + ], + "multiple": true, + "separator": { + "typ": "Literal", + "val": "/" + }, + "keywords": [] + }, + "border-top-left-radius": { + "shorthand": "border-radius" + }, + "border-top-right-radius": { + "shorthand": "border-radius" + }, + "border-bottom-right-radius": { + "shorthand": "border-radius" + }, + "border-bottom-left-radius": { + "shorthand": "border-radius" + }, + "border-width": { + "shorthand": "border-width", + "map": "border", + "properties": [ + "border-top-width", + "border-right-width", + "border-bottom-width", + "border-left-width" + ], + "types": [ + "Length", + "Perc" + ], + "default": [ + "medium" + ], + "keywords": [ + "thin", + "medium", + "thick" + ] + }, + "border-top-width": { + "map": "border", + "shorthand": "border-width" + }, + "border-right-width": { + "map": "border", + "shorthand": "border-width" + }, + "border-bottom-width": { + "map": "border", + "shorthand": "border-width" + }, + "border-left-width": { + "map": "border", + "shorthand": "border-width" + }, + "border-style": { + "shorthand": "border-style", + "map": "border", + "properties": [ + "border-top-style", + "border-right-style", + "border-bottom-style", + "border-left-style" + ], + "types": [], + "default": [ + "none" + ], + "keywords": [ + "none", + "hidden", + "dotted", + "dashed", + "solid", + "double", + "groove", + "ridge", + "inset", + "outset" + ] + }, + "border-top-style": { + "map": "border", + "shorthand": "border-style" + }, + "border-right-style": { + "map": "border", + "shorthand": "border-style" + }, + "border-bottom-style": { + "map": "border", + "shorthand": "border-style" + }, + "border-left-style": { + "map": "border", + "shorthand": "border-style" + }, + "border-color": { + "shorthand": "border-color", + "map": "border", + "properties": [ + "border-top-color", + "border-right-color", + "border-bottom-color", + "border-left-color" + ], + "types": [ + "Color" + ], + "default": [ + "currentcolor" + ], + "keywords": [] + }, + "border-top-color": { + "map": "border", + "shorthand": "border-color" + }, + "border-right-color": { + "map": "border", + "shorthand": "border-color" + }, + "border-bottom-color": { + "map": "border", + "shorthand": "border-color" + }, + "border-left-color": { + "map": "border", + "shorthand": "border-color" + }, + "grid-row": { + "shorthand": "grid-row", + "properties": [ + "grid-row-start", + "grid-row-end" + ], + "types": [ + "Iden", + "Number" + ], + "multiple": true, + "valueSeparator": { + "typ": "Literal", + "val": "/" + }, + "default": [ + "auto" + ], + "keywords": [ + "auto", + "span" + ] + }, + "grid-row-start": { + "shorthand": "grid-row" + }, + "grid-row-end": { + "shorthand": "grid-row" + } + }, + "map": { + "flex-flow": { + "shorthand": "flex-flow", + "pattern": "flex-direction flex-wrap", + "keywords": [], + "default": [ + "row", + "nowrap" + ], + "properties": { + "flex-direction": { + "keywords": [ + "row", + "row-reverse", + "column", + "column-reverse" + ], + "default": [ + "row" + ], + "types": [] + }, + "flex-wrap": { + "keywords": [ + "wrap", + "nowrap", + "wrap-reverse" + ], + "default": [ + "nowrap" + ], + "types": [] + } + } + }, + "flex-direction": { + "shorthand": "flex-flow" + }, + "flex-wrap": { + "shorthand": "flex-flow" + }, + "container": { + "shorthand": "container", + "pattern": "container-name container-type", + "keywords": [], + "default": [], + "properties": { + "container-name": { + "required": true, + "multiple": true, + "keywords": [ + "none" + ], + "default": [ + "none" + ], + "types": [ + "Iden", + "DashedIden" + ] + }, + "container-type": { + "previous": "container-name", + "prefix": { + "typ": "Literal", + "val": "/" + }, + "keywords": [ + "size", + "inline-size", + "normal" + ], + "default": [ + "normal" + ], + "types": [] + } + } + }, + "container-name": { + "shorthand": "container" + }, + "container-type": { + "shorthand": "container" + }, + "flex": { + "shorthand": "flex", + "pattern": "flex-grow flex-shrink flex-basis", + "keywords": [ + "auto", + "none", + "initial" + ], + "default": [], + "mapping": { + "0 1 auto": "initial", + "0 0 auto": "none", + "1 1 auto": "auto" + }, + "properties": { + "flex-grow": { + "required": true, + "keywords": [], + "default": [], + "types": [ + "Number" + ] + }, + "flex-shrink": { + "keywords": [], + "default": [], + "types": [ + "Number" + ] + }, + "flex-basis": { + "keywords": [ + "max-content", + "min-content", + "fit-content", + "fit-content", + "content", + "auto" + ], + "default": [], + "types": [ + "Length", + "Perc" + ] + } + } + }, + "flex-grow": { + "shorthand": "flex" + }, + "flex-shrink": { + "shorthand": "flex" + }, + "flex-basis": { + "shorthand": "flex" + }, + "columns": { + "shorthand": "columns", + "pattern": "column-count column-width", + "keywords": [ + "auto" + ], + "default": [ + "auto", + "auto auto" + ], + "properties": { + "column-count": { + "keywords": [ + "auto" + ], + "default": [ + "auto" + ], + "types": [ + "Number" + ] + }, + "column-width": { + "keywords": [ + "auto" + ], + "default": [ + "auto" + ], + "types": [ + "Length" + ] + } + } + }, + "column-count": { + "shorthand": "columns" + }, + "column-width": { + "shorthand": "columns" + }, + "transition": { + "shorthand": "transition", + "multiple": true, + "separator": { + "typ": "Comma" + }, + "pattern": "transition-property transition-duration transition-timing-function transition-delay transition-behavior", + "keywords": [ + "none", + "all" + ], + "default": [ + "0s", + "0ms", + "all", + "ease", + "none", + "normal" + ], + "mapping": { + "cubic-bezier(.25,.1,.25,1)": "ease", + "cubic-bezier(0,0,1,1)": "linear", + "cubic-bezier(.42,0,1,1)": "ease-in", + "cubic-bezier(0,0,.58,1)": "ease-out", + "cubic-bezier(.42,0,.58,.42)": "ease-in-out" + }, + "properties": { + "transition-property": { + "keywords": [ + "none", + "all" + ], + "default": [ + "all" + ], + "types": [ + "Iden" + ] + }, + "transition-duration": { + "keywords": [], + "default": [ + "0s", + "0ms", + "normal" + ], + "types": [ + "Time" + ] + }, + "transition-timing-function": { + "keywords": [ + "ease", + "ease-in", + "ease-out", + "ease-in-out", + "linear", + "step-start", + "step-end" + ], + "default": [ + "ease" + ], + "types": [ + "TimingFunction" + ], + "mapping": { + "cubic-bezier(.25,.1,.25,1)": "ease", + "cubic-bezier(0,0,1,1)": "linear", + "cubic-bezier(.42,0,1,1)": "ease-in", + "cubic-bezier(0,0,.58,1)": "ease-out", + "cubic-bezier(.42,0,.58,.42)": "ease-in-out" + } + }, + "transition-delay": { + "keywords": [], + "default": [ + "0s" + ], + "types": [ + "Time" + ] + }, + "transition-behavior": { + "keywords": [ + "normal", + "allow-discrete" + ], + "default": [ + "normal" + ], + "types": [] + } + } + }, + "transition-property": { + "shorthand": "transition" + }, + "transition-duration": { + "shorthand": "transition" + }, + "transition-timing-function": { + "shorthand": "transition" + }, + "transition-delay": { + "shorthand": "transition" + }, + "transition-behavior": { + "shorthand": "transition" + }, + "animation": { + "shorthand": "animation", + "separator": { + "typ": "Comma" + }, + "pattern": "animation-name animation-duration animation-timing-function animation-delay animation-iteration-count animation-direction animation-fill-mode animation-play-state animation-timeline", + "default": [ + "1", + "0s", + "0ms", + "none", + "ease", + "normal", + "running", + "auto" + ], + "properties": { + "animation-name": { + "keywords": [ + "none" + ], + "default": [ + "none" + ], + "types": [ + "Iden" + ] + }, + "animation-duration": { + "keywords": [ + "auto" + ], + "default": [ + "0s", + "0ms", + "auto" + ], + "types": [ + "Time" + ], + "mapping": { + "auto": "0s" + } + }, + "animation-timing-function": { + "keywords": [ + "ease", + "ease-in", + "ease-out", + "ease-in-out", + "linear", + "step-start", + "step-end" + ], + "default": [ + "ease" + ], + "types": [ + "TimingFunction" + ], + "mapping": { + "cubic-bezier(.25,.1,.25,1)": "ease", + "cubic-bezier(0,0,1,1)": "linear", + "cubic-bezier(.42,0,1,1)": "ease-in", + "cubic-bezier(0,0,.58,1)": "ease-out", + "cubic-bezier(.42,0,.58,.42)": "ease-in-out" + } + }, + "animation-delay": { + "keywords": [], + "default": [ + "0s", + "0ms" + ], + "types": [ + "Time" + ] + }, + "animation-iteration-count": { + "keywords": [ + "infinite" + ], + "default": [ + "1" + ], + "types": [ + "Number" + ] + }, + "animation-direction": { + "keywords": [ + "normal", + "reverse", + "alternate", + "alternate-reverse" + ], + "default": [ + "normal" + ], + "types": [] + }, + "animation-fill-mode": { + "keywords": [ + "none", + "forwards", + "backwards", + "both" + ], + "default": [ + "none" + ], + "types": [] + }, + "animation-play-state": { + "keywords": [ + "running", + "paused" + ], + "default": [ + "running" + ], + "types": [] + }, + "animation-timeline": { + "keywords": [ + "none", + "auto" + ], + "default": [ + "auto" + ], + "types": [ + "DashedIden", + "TimelineFunction" + ] + } + } + }, + "animation-name": { + "shorthand": "animation" + }, + "animation-duration": { + "shorthand": "animation" + }, + "animation-timing-function": { + "shorthand": "animation" + }, + "animation-delay": { + "shorthand": "animation" + }, + "animation-iteration-count": { + "shorthand": "animation" + }, + "animation-direction": { + "shorthand": "animation" + }, + "animation-fill-mode": { + "shorthand": "animation" + }, + "animation-play-state": { + "shorthand": "animation" + }, + "animation-timeline": { + "shorthand": "animation" + }, + "text-emphasis": { + "shorthand": "text-emphasis", + "pattern": "text-emphasis-color text-emphasis-style", + "default": [ + "none", + "currentcolor" + ], + "properties": { + "text-emphasis-style": { + "keywords": [ + "none", + "filled", + "open", + "dot", + "circle", + "double-circle", + "triangle", + "sesame" + ], + "default": [ + "none" + ], + "types": [ + "String" + ] + }, + "text-emphasis-color": { + "default": [ + "currentcolor" + ], + "types": [ + "Color" + ] + } + } + }, + "text-emphasis-style": { + "shorthand": "text-emphasis" + }, + "text-emphasis-color": { + "shorthand": "text-emphasis" + }, + "border": { + "shorthand": "border", + "pattern": "border-color border-style border-width", + "keywords": [ + "none" + ], + "default": [ + "0", + "none" + ], + "properties": { + "border-color": { + "types": [ + "Color" + ], + "default": [ + "currentcolor" + ], + "keywords": [] + }, + "border-style": { + "types": [], + "default": [ + "none" + ], + "keywords": [ + "none", + "hidden", + "dotted", + "dashed", + "solid", + "double", + "groove", + "ridge", + "inset", + "outset" + ] + }, + "border-width": { + "types": [ + "Length", + "Perc" + ], + "default": [ + "medium" + ], + "keywords": [ + "thin", + "medium", + "thick" + ] + } + } + }, + "border-color": { + "shorthand": "border" + }, + "border-style": { + "shorthand": "border" + }, + "border-width": { + "shorthand": "border" + }, + "list-style": { + "shorthand": "list-style", + "pattern": "list-style-type list-style-position list-style-image", + "keywords": [ + "none", + "outside" + ], + "default": [ + "none", + "outside" + ], + "properties": { + "list-style-position": { + "types": [], + "default": [ + "outside" + ], + "keywords": [ + "inside", + "outside" + ] + }, + "list-style-image": { + "default": [ + "none" + ], + "keywords": [ + "node" + ], + "types": [ + "UrlFunc", + "ImageFunc" + ] + }, + "list-style-type": { + "types": [ + "String", + "Iden", + "Symbols" + ], + "default": [ + "disc" + ], + "keywords": [ + "disc", + "circle", + "square", + "decimal", + "decimal-leading-zero", + "lower-roman", + "upper-roman", + "lower-greek", + "lower-latin", + "upper-latin", + "none" + ] + } + } + }, + "list-style-position": { + "shorthand": "list-style" + }, + "list-style-image": { + "shorthand": "list-style" + }, + "list-style-type": { + "shorthand": "list-style" + }, + "overflow": { + "shorthand": "overflow", + "pattern": "overflow-x overflow-y", + "keywords": [ + "auto", + "visible", + "hidden", + "clip", + "scroll" + ], + "default": [], + "mapping": { + "visible visible": "visible", + "auto auto": "auto", + "hidden hidden": "hidden", + "scroll scroll": "scroll" + }, + "properties": { + "overflow-x": { + "default": [], + "types": [], + "keywords": [ + "auto", + "visible", + "hidden", + "clip", + "scroll" + ] + }, + "overflow-y": { + "default": [], + "types": [], + "keywords": [ + "auto", + "visible", + "hidden", + "clip", + "scroll" + ] + } + } + }, + "overflow-x": { + "shorthand": "overflow" + }, + "overflow-y": { + "shorthand": "overflow" + }, + "outline": { + "shorthand": "outline", + "pattern": "outline-color outline-style outline-width", + "keywords": [ + "none" + ], + "default": [ + "0", + "none", + "currentcolor" + ], + "properties": { + "outline-color": { + "types": [ + "Color" + ], + "default": [ + "currentcolor" + ], + "keywords": [ + "currentcolor" + ] + }, + "outline-style": { + "types": [], + "default": [ + "none" + ], + "keywords": [ + "auto", + "none", + "dotted", + "dashed", + "solid", + "double", + "groove", + "ridge", + "inset", + "outset" + ] + }, + "outline-width": { + "types": [ + "Length", + "Perc" + ], + "default": [ + "medium" + ], + "keywords": [ + "thin", + "medium", + "thick" + ] + } + } + }, + "outline-color": { + "shorthand": "outline" + }, + "outline-style": { + "shorthand": "outline" + }, + "outline-width": { + "shorthand": "outline" + }, + "font": { + "shorthand": "font", + "pattern": "font-weight font-style font-size line-height font-stretch font-variant font-family", + "keywords": [ + "caption", + "icon", + "menu", + "message-box", + "small-caption", + "status-bar", + "-moz-window, ", + "-moz-document, ", + "-moz-desktop, ", + "-moz-info, ", + "-moz-dialog", + "-moz-button", + "-moz-pull-down-menu", + "-moz-list", + "-moz-field" + ], + "default": [], + "properties": { + "font-weight": { + "types": [ + "Number" + ], + "default": [ + "400", + "normal" + ], + "keywords": [ + "normal", + "bold", + "lighter", + "bolder" + ], + "constraints": { + "value": { + "min": "1", + "max": "1000" + } + }, + "mapping": { + "thin": "100", + "hairline": "100", + "extra light": "200", + "ultra light": "200", + "light": "300", + "normal": "400", + "regular": "400", + "medium": "500", + "semi bold": "600", + "demi bold": "600", + "bold": "700", + "extra bold": "800", + "ultra bold": "800", + "black": "900", + "heavy": "900", + "extra black": "950", + "ultra black": "950" + } + }, + "font-style": { + "types": [ + "Angle" + ], + "default": [ + "normal" + ], + "keywords": [ + "normal", + "italic", + "oblique" + ] + }, + "font-size": { + "types": [ + "Length", + "Perc" + ], + "default": [], + "keywords": [ + "xx-small", + "x-small", + "small", + "medium", + "large", + "x-large", + "xx-large", + "xxx-large", + "larger", + "smaller" + ], + "required": true + }, + "line-height": { + "types": [ + "Length", + "Perc", + "Number" + ], + "default": [ + "normal" + ], + "keywords": [ + "normal" + ], + "previous": "font-size", + "prefix": { + "typ": "Literal", + "val": "/" + } + }, + "font-stretch": { + "types": [ + "Perc" + ], + "default": [ + "normal" + ], + "keywords": [ + "ultra-condensed", + "extra-condensed", + "condensed", + "semi-condensed", + "normal", + "semi-expanded", + "expanded", + "extra-expanded", + "ultra-expanded" + ], + "mapping": { + "ultra-condensed": "50%", + "extra-condensed": "62.5%", + "condensed": "75%", + "semi-condensed": "87.5%", + "normal": "100%", + "semi-expanded": "112.5%", + "expanded": "125%", + "extra-expanded": "150%", + "ultra-expanded": "200%" + } + }, + "font-variant": { + "types": [], + "default": [ + "normal" + ], + "keywords": [ + "normal", + "none", + "common-ligatures", + "no-common-ligatures", + "discretionary-ligatures", + "no-discretionary-ligatures", + "historical-ligatures", + "no-historical-ligatures", + "contextual", + "no-contextual", + "historical-forms", + "small-caps", + "all-small-caps", + "petite-caps", + "all-petite-caps", + "unicase", + "titling-caps", + "ordinal", + "slashed-zero", + "lining-nums", + "oldstyle-nums", + "proportional-nums", + "tabular-nums", + "diagonal-fractions", + "stacked-fractions", + "ordinal", + "slashed-zero", + "ruby", + "jis78", + "jis83", + "jis90", + "jis04", + "simplified", + "traditional", + "full-width", + "proportional-width", + "ruby", + "sub", + "super", + "text", + "emoji", + "unicode" + ] + }, + "font-family": { + "types": [ + "String", + "Iden" + ], + "default": [], + "keywords": [ + "serif", + "sans-serif", + "monospace", + "cursive", + "fantasy", + "system-ui", + "ui-serif", + "ui-sans-serif", + "ui-monospace", + "ui-rounded", + "math", + "emoji", + "fangsong" + ], + "required": true, + "multiple": true, + "separator": { + "typ": "Comma" + } + } + } + }, + "font-weight": { + "shorthand": "font" + }, + "font-style": { + "shorthand": "font" + }, + "font-size": { + "shorthand": "font" + }, + "line-height": { + "shorthand": "font" + }, + "font-stretch": { + "shorthand": "font" + }, + "font-variant": { + "shorthand": "font" + }, + "font-family": { + "shorthand": "font" + }, + "background": { + "shorthand": "background", + "pattern": "background-attachment background-origin background-clip background-color background-image background-repeat background-position background-size", + "keywords": [ + "none" + ], + "default": [ + "0 0", + "none", + "auto", + "repeat", + "transparent", + "#0000", + "scroll", + "padding-box", + "border-box" + ], + "multiple": true, + "set": { + "background-origin": [ + "background-clip" + ] + }, + "separator": { + "typ": "Comma" + }, + "properties": { + "background-repeat": { + "types": [], + "default": [ + "repeat" + ], + "multiple": true, + "keywords": [ + "repeat-x", + "repeat-y", + "repeat", + "space", + "round", + "no-repeat" + ], + "mapping": { + "repeat no-repeat": "repeat-x", + "no-repeat repeat": "repeat-y", + "repeat repeat": "repeat", + "space space": "space", + "round round": "round", + "no-repeat no-repeat": "no-repeat" + } + }, + "background-color": { + "types": [ + "Color" + ], + "default": [ + "#0000", + "transparent" + ], + "multiple": true, + "keywords": [] + }, + "background-image": { + "types": [ + "UrlFunc", + "ImageFunc" + ], + "default": [ + "none" + ], + "keywords": [ + "none" + ] + }, + "background-attachment": { + "types": [], + "default": [ + "scroll" + ], + "multiple": true, + "keywords": [ + "scroll", + "fixed", + "local" + ] + }, + "background-clip": { + "types": [], + "default": [ + "border-box" + ], + "multiple": true, + "keywords": [ + "border-box", + "padding-box", + "content-box", + "text" + ] + }, + "background-origin": { + "types": [], + "default": [ + "padding-box" + ], + "multiple": true, + "keywords": [ + "border-box", + "padding-box", + "content-box" + ] + }, + "background-position": { + "multiple": true, + "types": [ + "Perc", + "Length" + ], + "default": [ + "0 0", + "top left", + "left top" + ], + "keywords": [ + "top", + "left", + "center", + "bottom", + "right" + ], + "mapping": { + "left": "0", + "top": "0", + "center": "50%", + "center center": "50%", + "50% 50%": "50%", + "bottom": "100%", + "right": "100%" + }, + "constraints": { + "mapping": { + "max": 2 + } + } + }, + "background-size": { + "multiple": true, + "previous": "background-position", + "prefix": { + "typ": "Literal", + "val": "/" + }, + "types": [ + "Perc", + "Length" + ], + "default": [ + "auto", + "auto auto" + ], + "keywords": [ + "auto", + "cover", + "contain" + ], + "mapping": { + "auto auto": "auto" + } + } + } + }, + "background-repeat": { + "shorthand": "background" + }, + "background-color": { + "shorthand": "background" + }, + "background-image": { + "shorthand": "background" + }, + "background-attachment": { + "shorthand": "background" + }, + "background-clip": { + "shorthand": "background" + }, + "background-origin": { + "shorthand": "background" + }, + "background-position": { + "shorthand": "background" + }, + "background-size": { + "shorthand": "background" + } + }, + "property": { + "transform-origin": { + "pattern": [ + [ + "left|center|right", + "top|center|bottom", + "" + ], + [ + "left|center|right", + "top|center|bottom" + ], + [ + "center|left|right|center|top|bottom|" + ] + ], + "mapping": { + "center": { + "typ": "Perc", + "val": 50 + }, + "left": { + "typ": "Perc", + "val": 0 + }, + "right": { + "typ": "Perc", + "val": 100 + }, + "top": { + "typ": "Perc", + "val": 0 + }, + "bottom": { + "typ": "Perc", + "val": 100 + } + } + } + } +} \ No newline at end of file diff --git a/src/lib/parser/declaration/map.ts b/src/lib/parser/declaration/map.ts index 23f02650..c6d1a79e 100644 --- a/src/lib/parser/declaration/map.ts +++ b/src/lib/parser/declaration/map.ts @@ -387,7 +387,6 @@ export class PropertyMap { const filtered: AstDeclaration[] = []; for (const declaration of values) { - dec = removeDefaults(declaration); for (const t of dec.val) { @@ -395,8 +394,11 @@ export class PropertyMap { isImportant = true; } - if (filtered.length == 0 && t.typ != EnumToken.WhitespaceTokenType && t.typ != EnumToken.ImportantTokenType) { - + if ( + filtered.length == 0 && + t.typ != EnumToken.WhitespaceTokenType && + t.typ != EnumToken.ImportantTokenType + ) { filtered.push(dec); } } diff --git a/src/lib/parser/parse.ts b/src/lib/parser/parse.ts index cd6980b2..2742b020 100644 --- a/src/lib/parser/parse.ts +++ b/src/lib/parser/parse.ts @@ -1958,7 +1958,6 @@ export async function doParse( options.sourcesMap!.set(source.id, source); const parseInfo = { stream, - buffer: "", offset: 0, source, position: 0, @@ -2538,7 +2537,6 @@ export async function doParse( const root: ParseResult = await doParse( stream instanceof ReadableStream ? tokenizeStream(stream, { - buffer: "", offset: 0, source: new SourceFile("", [], src.relative), position: 0, @@ -2546,7 +2544,6 @@ export async function doParse( } as ParseInfo) : tokenize({ stream, - buffer: "", offset: 0, position: 0, source: new SourceFile(stream, [], src.relative), @@ -4195,7 +4192,6 @@ export async function parseDeclarations(declaration: string): Promise', GREATER THAN } export function consumeString(parseInfo: ParseInfo): Array { - const quote = next(parseInfo); - let value: string; - let buffer: string = quote; + const quote: number = next(parseInfo).charCodeAt(0); + let charCode: number; + let decodeSegments: boolean = false; const result: Array = []; - while ((value = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset))) { - if (value == "\\") { - if ("\\" == parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset + 1)) { - buffer += next(parseInfo, 2); + while ((charCode = parseInfo.stream.charCodeAt(parseInfo.currentPosition - parseInfo.offset)) == charCode) { + if (charCode == TokenMap.REVERSE_SOLIDUS) { + if (charCode == parseInfo.stream.charCodeAt(parseInfo.currentPosition - parseInfo.offset + 1)) { + next(parseInfo, 2); continue; } - const sequence: string = peek(parseInfo, 6); + const sequence: string = peek(parseInfo, 7); let escapeSequence: string = ""; let codepoint: number; let i; @@ -221,68 +222,77 @@ export function consumeString(parseInfo: ParseInfo): Array { } if (escapeSequence.trimEnd().length > 0) { - const codepoint = parseInt(escapeSequence, 16); + // const codepoint = parseInt(escapeSequence, 16); + + // TODO set decode flag ON + // if ( + // codepoint == 0 || + // // leading surrogate + // (0xd800 <= codepoint && codepoint <= 0xdbff) || + // // trailing surrogate + // (0xdc00 <= codepoint && codepoint <= 0xdfff) + // ) { + // buffer += String.fromCodePoint(0xfffd); + // } else { + // buffer += String.fromCodePoint(codepoint); + // } + + const length: number = + escapeSequence.length + + 1 + + (isWhiteSpace(parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset)?.charCodeAt(0)) + ? 1 + : 0); - if ( - codepoint == 0 || - // leading surrogate - (0xd800 <= codepoint && codepoint <= 0xdbff) || - // trailing surrogate - (0xdc00 <= codepoint && codepoint <= 0xdfff) - ) { - buffer += String.fromCodePoint(0xfffd); - } else { - buffer += String.fromCodePoint(codepoint); - } + decodeSegments = true; - next( - parseInfo, - escapeSequence.length + - 1 + - (isWhiteSpace( - parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset)?.charCodeAt(0), - ) - ? 1 - : 0), - ); + next(parseInfo, length); continue; } - buffer += next(parseInfo, 2); + next(parseInfo, 2); continue; } - if (value == quote) { - buffer += value; + if (charCode == quote) { + next(parseInfo); result.push( yieldResult( - buffer, parseInfo, /* hasNewLine ? EnumToken.BadStringTokenType : */ EnumToken.StringTokenType, + decodeSegments ? { decodeSegments } : null, ), ); - next(parseInfo); - buffer = ""; + return result; } - if (isNewLine(value.charCodeAt(0))) { - result.push(yieldResult(buffer + next(parseInfo), parseInfo, EnumToken.BadStringTokenType)); + if (isNewLine(charCode)) { + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.BadStringTokenType)); return result; } - buffer += value; next(parseInfo); } // EOF - 'Unclosed-string' fixed - result.push(yieldResult(buffer + quote, parseInfo, EnumToken.StringTokenType)); + result.push(yieldResult(parseInfo, EnumToken.StringTokenType)); return result; } -export function yieldResult(val: string, parseInfo: ParseInfo, hint?: EnumToken): TokenizeResult { +export function yieldResult( + parseInfo: ParseInfo, + hint?: EnumToken, + options?: { decodeSegments: boolean } | null, +): TokenizeResult { + let val: string = parseInfo.stream.slice( + parseInfo.position - parseInfo.offset, + parseInfo.currentPosition - parseInfo.offset, + ); + let token: Token | null = null; let dimension: | DimensionToken @@ -294,7 +304,24 @@ export function yieldResult(val: string, parseInfo: ParseInfo, hint?: EnumToken) | FrequencyToken | null; - // console.debug(`Yield result: ${val}, ${hint}`); + if (options?.decodeSegments) { + val = val.replace(/\\([0-9a-fA-F]{1,6})(?:\s)?/g, (_, sequence) => { + const codepoint = parseInt(sequence, 16); + + if ( + codepoint == 0 || + // leading surrogate + (0xd800 <= codepoint && codepoint <= 0xdbff) || + // trailing surrogate + (0xdc00 <= codepoint && codepoint <= 0xdfff) || + codepoint > 0x10ffff + ) { + return "\uFFFD"; + } + + return String.fromCodePoint(codepoint); + }); + } if (hint != null) { let searchArray: string[] | null = null; @@ -476,6 +503,152 @@ export function next(parseInfo: ParseInfo, count: number = 1): string { parseInfo.currentPosition += char.length; return char; } +function isIdentToken(parseInfo: ParseInfo, start?: number, end?: number): boolean { + let j: number = parseInfo.currentPosition - parseInfo.offset; + let i: number = parseInfo.position - parseInfo.offset; + + if (start != null) { + if (end == null) { + if (start < 0) { + j += start; + } else { + i += start; + } + } else { + if (end < 0) { + j += end; + } else { + j = parseInfo.position + end; + } + } + } + + j--; + + let codepoint: number = parseInfo.stream.charCodeAt(i) as number; + + // - + if (codepoint == 0x2d) { + let nextCodepoint: number; + + if ((nextCodepoint = parseInfo.stream.charCodeAt(i + 1)) != nextCodepoint) { + return false; + } + + if (isDigit(nextCodepoint)) { + return false; + } + + codepoint = nextCodepoint; + i++; + } + + if (codepoint !== 0x2d && !isIdentStart(codepoint)) { + return false; + } + + if (codepoint == TokenMap.REVERSE_SOLIDUS) { + codepoint = parseInfo.stream.charCodeAt(i + 1) as number; + + // if (!isIdentCodepoint(codepoint)) { + // return false; + // } + i += String.fromCodePoint(codepoint).length; + + // if (i < j) { + // codepoint = name.charCodeAt(i) as number; + + // if (!isIdentCodepoint(codepoint)) { + // return false; + // } + // } + } + + while (i < j) { + i += codepoint < 0x80 ? 1 : String.fromCodePoint(codepoint).length; + codepoint = parseInfo.stream.charCodeAt(i) as number; + + if (codepoint == TokenMap.REVERSE_SOLIDUS) { + i += codepoint < 0x80 ? 1 : String.fromCodePoint(codepoint).length; + codepoint = parseInfo.stream.charCodeAt(i) as number; + i += codepoint < 0x80 ? 1 : String.fromCodePoint(codepoint).length; + + continue; + } + + if (codepoint !== 0x2d && !isIdentCodepoint(codepoint)) { + return false; + } + } + + return true; +} + +function isPseudo(parseInfo: ParseInfo): boolean { + let position: number = parseInfo.currentPosition - parseInfo.offset; + let endPosition: number = parseInfo.currentPosition - parseInfo.offset; + return (parseInfo.stream.charAt(position) == ":" && + parseInfo.stream.charAt(endPosition - 1) == "(" && + (parseInfo.stream.charAt(position + 1) == ":" + ? isIdentToken(parseInfo, 2, -1) + : isIdentToken(parseInfo, 1, -1))) || + parseInfo.stream.charAt(position + 1) == ":" + ? isIdentToken(parseInfo, 2) + : isIdentToken(parseInfo, 1); +} + +function startsWith(parseInfo: ParseInfo, input: string): boolean { + let i: number = 0; + let j: number = input.length; + + while (i < j) { + if (parseInfo.stream.charAt(parseInfo.position - parseInfo.offset + i) != input.charAt(i)) { + return false; + } + i++; + } + + return true; +} + +function isURLToken(parseInfo: ParseInfo): boolean { + let i: number = parseInfo.position - parseInfo.offset; + let c: number; + + while (++i < parseInfo.currentPosition) { + c = parseInfo.stream.charCodeAt(i) as number; + + // single quote or double quote or start parenthesis or close parenthesis + if (isNonPrintable(c) || c == 0x27 || c == 0x22 || c == 0x28 || c == 0x29) { + return false; + } + + // valid escape + if (c == TokenMap.REVERSE_SOLIDUS) { + i++; + + if (i >= parseInfo.currentPosition) { + return false; + } + + c = parseInfo.stream.charCodeAt(i) as number; + + // c is not '\n' or '\r' or '\f' + if (c == 0x6e || c == 0x72 || c == 0x66) { + return false; + } + + continue; + } + + // is white space + if (c == 0x20 || c == 0x09) { + break; + } + } + + return i == parseInfo.currentPosition; +} /** * Tokenize CSS string @@ -485,7 +658,6 @@ export function next(parseInfo: ParseInfo, count: number = 1): string { export function tokenize(parseInfo: ParseInfo | string, yieldEOFToken: boolean = true): Array { if (typeof parseInfo == "string") { parseInfo = { - buffer: "", stream: parseInfo, source: new SourceFile(parseInfo, [], ""), offset: 0, @@ -495,134 +667,147 @@ export function tokenize(parseInfo: ParseInfo | string, yieldEOFToken: boolean = }; } - let value: string; - let buffer: string = parseInfo.buffer; let charCode: number; let nextCharCode: number; const startTime: number = performance.now(); const result: TokenizeResult[] = []; // allow 10 characters buffer for the streaming parser to avoid incomplete tokens - const endPosition: number = parseInfo.stream.length - 10; - - parseInfo.buffer = ""; - - while ((value = peek(parseInfo))) { - charCode = value.charCodeAt(0); + const endPosition: number = parseInfo.stream.length - 1; + // NaN is not equal to NaN + while ((charCode = peek(parseInfo).charCodeAt(0)) == charCode) { switch (charCode) { case TokenMap.EQUALS: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, EnumToken.DelimTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.DelimTokenType)); break; // '+' or '-' case TokenMap.PLUS: case TokenMap.MINUS: - next(parseInfo); + nextCharCode = peek(parseInfo).charCodeAt(0); - if (charCode === TokenMap.PLUS && !isNumber(peek(parseInfo))) { - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + // not a number + if (charCode === TokenMap.PLUS && !(nextCharCode >= 0x30 && nextCharCode <= 0x39)) { + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(value, parseInfo, SymbolsMapTokens[value])); + next(parseInfo); + + result.push( + yieldResult( + parseInfo, + SymbolsMapTokens[ + parseInfo.stream + .slice( + parseInfo.position - parseInfo.offset, + parseInfo.currentPosition - parseInfo.offset, + ) + .toLowerCase() + ], + ), + ); break; } - buffer += value; + next(parseInfo); + break; // '{' case TokenMap.LEFT_BRACE: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, EnumToken.BlockStartTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.BlockStartTokenType)); break; // '}' case TokenMap.RIGHT_BRACE: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, EnumToken.BlockEndTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.BlockEndTokenType)); break; // '(' case TokenMap.LEFT_PARENTHESIS: - if (buffer.length > 0) { - if (buffer[0] === ":" && isPseudo(buffer)) { + if (parseInfo.position < parseInfo.currentPosition) { + if (parseInfo.stream[parseInfo.position - parseInfo.offset] === ":" && isPseudo(parseInfo)) { next(parseInfo); - result.push(yieldResult(buffer, parseInfo, EnumToken.PseudoClassFunctionTokenDefType)); - buffer = ""; + result.push(yieldResult(parseInfo, EnumToken.PseudoClassFunctionTokenDefType)); + break; - } else if (isIdent(buffer)) { - const hint: EnumToken = buffer.startsWith("--") + } else if (isIdentToken(parseInfo)) { + const hint: EnumToken = startsWith(parseInfo, "--") ? EnumToken.CustomFunctionTokenDefType - : (SymbolsMapTokens[buffer.toLowerCase() + "("] ?? EnumToken.FunctionTokenDefType); - - result.push(yieldResult(buffer, parseInfo, hint)); + : (SymbolsMapTokens[ + parseInfo.stream + .slice( + parseInfo.position - parseInfo.offset, + parseInfo.currentPosition - parseInfo.offset, + ) + .toLowerCase() + "(" + ] ?? EnumToken.FunctionTokenDefType); + + result.push(yieldResult(parseInfo, hint)); next(parseInfo); - buffer = ""; - if (hint === EnumToken.UrlFunctionTokenDefType) { - buffer = ""; - value = peek(parseInfo); + // consume '(' + parseInfo.position = parseInfo.currentPosition; + if (hint === EnumToken.UrlFunctionTokenDefType) { // consume an while (isWhiteSpace(peek(parseInfo).charCodeAt(0))) { - // buffer += next(parseInfo); next(parseInfo); - // charCode = value.charCodeAt(0); } - value = peek(parseInfo); + charCode = peek(parseInfo).charCodeAt(0); let values: Array | null = null; - if (value == '"' || value == "'") { + if (charCode == TokenMap.DOUBLE_QUOTE || charCode == TokenMap.SINGLE_QUOTE) { values = consumeString(parseInfo); } else { do { - buffer += next(parseInfo); - value = peek(parseInfo); - charCode = value.charCodeAt(0); + next(parseInfo); + // value = peek(parseInfo); + charCode = peek(parseInfo).charCodeAt(0); } while ( // !(value === "/" && match(parseInfo, "/*") && - value !== ")" && - value !== "" + charCode !== TokenMap.RIGHT_PARENTHESIS && + parseInfo.currentPosition < endPosition ); } if (values != null) { - if (peek(parseInfo) === "") { + // NaN is not equal to NaN + if ((charCode = peek(parseInfo).charCodeAt(0)) != charCode) { for (let i = 0; i < values.length; i++) { values[i].token.typ = EnumToken.BadUrlTokenType; } } result.push(...values); - } else if (buffer.length > 0) { + } else if (parseInfo.position < parseInfo.currentPosition) { result.push( yieldResult( - buffer.trimEnd(), parseInfo, - // buffer.length > 0 - peek(parseInfo) === "" || !isURLToken(buffer) + // parseInfo.position < parseInfo.currentPosition + (charCode = peek(parseInfo).charCodeAt(0)) != charCode || !isURLToken(parseInfo) ? EnumToken.BadUrlTokenType : EnumToken.UrlTokenTokenType, ), ); - buffer = ""; } } @@ -630,61 +815,63 @@ export function tokenize(parseInfo: ParseInfo | string, yieldEOFToken: boolean = } } - result.push(yieldResult(next(parseInfo), parseInfo, EnumToken.StartParensTokenType)); - buffer = ""; + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.StartParensTokenType)); + break; // ')' case TokenMap.RIGHT_PARENTHESIS: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, EnumToken.EndParensTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.EndParensTokenType)); break; // '[' case TokenMap.LEFT_BRACKETS: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, EnumToken.AttrStartTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.AttrStartTokenType)); break; // ']' case TokenMap.RIGHT_BRACKETS: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, EnumToken.AttrEndTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.AttrEndTokenType)); break; case TokenMap.SEMICOLON: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, EnumToken.SemiColonTokenType)); + + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.SemiColonTokenType)); break; case TokenMap.COLON: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } next(parseInfo); if (peek(parseInfo).charCodeAt(0) == TokenMap.COLON) { - result.push(yieldResult(value + next(parseInfo), parseInfo, EnumToken.DoubleColonTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.DoubleColonTokenType)); break; } - result.push(yieldResult(value, parseInfo, EnumToken.ColonTokenType)); + result.push(yieldResult(parseInfo, EnumToken.ColonTokenType)); break; // \n \r \f \v \t space @@ -696,12 +883,11 @@ export function tokenize(parseInfo: ParseInfo | string, yieldEOFToken: boolean = case 0xd: case 0x2028: case 0x2029: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - buffer += next(parseInfo); + next(parseInfo); nextCharCode = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset).charCodeAt(0); while ( @@ -710,240 +896,252 @@ export function tokenize(parseInfo: ParseInfo | string, yieldEOFToken: boolean = nextCharCode == 0x2028 || nextCharCode == 0x2029 ) { - value += next(parseInfo); + next(parseInfo); nextCharCode = parseInfo.stream.charAt(parseInfo.currentPosition - parseInfo.offset).charCodeAt(0); } - result.push(yieldResult(value, parseInfo, EnumToken.WhitespaceTokenType)); - buffer = ""; + result.push(yieldResult(parseInfo, EnumToken.WhitespaceTokenType)); + break; case TokenMap.COMMA: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, EnumToken.CommaTokenType)); + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.CommaTokenType)); break; case TokenMap.DOLLAR: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } if (match(parseInfo, "$=")) { - result.push(yieldResult(next(parseInfo, 2), parseInfo, EnumToken.EndMatchTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, EnumToken.EndMatchTokenType)); break; } - buffer += next(parseInfo); + next(parseInfo); break; case TokenMap.TILDA: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } if (match(parseInfo, "~=")) { - result.push(yieldResult(next(parseInfo, 2), parseInfo, EnumToken.IncludeMatchTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, EnumToken.IncludeMatchTokenType)); break; } - result.push(yieldResult(next(parseInfo), parseInfo, EnumToken.Tilda)); - buffer = ""; + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.Tilda)); + break; // case '^': case TokenMap.CARET: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } if (match(parseInfo, "^=")) { - result.push(yieldResult(next(parseInfo, 2), parseInfo, EnumToken.StartMatchTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, EnumToken.StartMatchTokenType)); break; } - buffer += next(parseInfo); + next(parseInfo); break; case TokenMap.STAR: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } if (match(parseInfo, "*=")) { - result.push(yieldResult(next(parseInfo, 2), parseInfo, EnumToken.ContainMatchTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, EnumToken.ContainMatchTokenType)); break; } - result.push(yieldResult(next(parseInfo), parseInfo, EnumToken.Star)); - buffer = ""; + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.Star)); + break; case TokenMap.AMPERSAND: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult(next(parseInfo), parseInfo, EnumToken.NestingSelectorTokenType)); - buffer = ""; + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.NestingSelectorTokenType)); + break; case TokenMap.PIPE: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } // '||' if (match(parseInfo, "||")) { - result.push(yieldResult(next(parseInfo, 2), parseInfo, EnumToken.ColumnCombinatorTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, EnumToken.ColumnCombinatorTokenType)); break; } else if (match(parseInfo, "|=")) { - result.push(yieldResult(next(parseInfo, 2), parseInfo, EnumToken.DashMatchTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, EnumToken.DashMatchTokenType)); break; } - result.push(yieldResult(next(parseInfo), parseInfo, EnumToken.Pipe)); - buffer = ""; + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.Pipe)); + break; case TokenMap.EXCLAMATION: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } if (match(parseInfo, "!important")) { - result.push(yieldResult(next(parseInfo, 10), parseInfo, EnumToken.ImportantTokenType)); - buffer = ""; + next(parseInfo, 10); + result.push(yieldResult(parseInfo, EnumToken.ImportantTokenType)); + break; } - buffer += next(parseInfo); + next(parseInfo); break; case TokenMap.SLASH: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } if (!match(parseInfo, "/*")) { - result.push(yieldResult(next(parseInfo), parseInfo, SymbolsMapTokens[value])); + next(parseInfo); + result.push( + yieldResult( + parseInfo, + SymbolsMapTokens[parseInfo.stream.slice(parseInfo.position, parseInfo.currentPosition)], + ), + ); break; } - buffer += next(parseInfo, 2); - - while ((value = next(parseInfo))) { - if (value == "*") { - buffer += value; + next(parseInfo, 2); + while ((charCode = next(parseInfo).charCodeAt(0)) == charCode) { + if (charCode == TokenMap.STAR) { if (match(parseInfo, "/")) { - result.push(yieldResult(buffer + next(parseInfo), parseInfo, EnumToken.CommentTokenType)); - buffer = ""; + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.CommentTokenType)); + break; } - } else { - buffer += value; } + // else { + // buffer += value; + // } } - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo, EnumToken.BadCommentTokenType)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo, EnumToken.BadCommentTokenType)); } break; case TokenMap.GREATERTHAN: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } if (match(parseInfo, ">=")) { - result.push(yieldResult(next(parseInfo, 2), parseInfo, EnumToken.GteTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, EnumToken.GteTokenType)); break; } - result.push(yieldResult(next(parseInfo), parseInfo, EnumToken.GtTokenType)); - buffer = ""; + next(parseInfo); + result.push(yieldResult(parseInfo, EnumToken.GtTokenType)); + break; case TokenMap.LOWERTHAN: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } if (match(parseInfo, "<=")) { - result.push(yieldResult(next(parseInfo, 2), parseInfo, EnumToken.LteTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, EnumToken.LteTokenType)); break; } - buffer += next(parseInfo); + next(parseInfo); if (match(parseInfo, "!--")) { - buffer += next(parseInfo, 3); + next(parseInfo, 3); - while ((value = next(parseInfo))) { - buffer += value; - if (value == "-" && match(parseInfo, "->")) { + while ((charCode = next(parseInfo).charCodeAt(0)) == charCode) { + if (charCode == TokenMap.MINUS && match(parseInfo, "->")) { break; } } - if (value === "") { - result.push(yieldResult(buffer, parseInfo, EnumToken.BadCdoTokenType)); + if (parseInfo.currentPosition >= endPosition) { + result.push(yieldResult(parseInfo, EnumToken.BadCdoTokenType)); } else { - result.push(yieldResult(buffer + next(parseInfo, 2), parseInfo, EnumToken.CDOCOMMTokenType)); + next(parseInfo, 2); + result.push(yieldResult(parseInfo, EnumToken.CDOCOMMTokenType)); } - - buffer = ""; } break; case TokenMap.HASH: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - buffer += next(parseInfo); + next(parseInfo); break; case TokenMap.REVERSE_SOLIDUS: + if (!yieldEOFToken && parseInfo.stream.length == parseInfo.currentPosition - parseInfo.offset + 1) { + break; + } + next(parseInfo); // EOF if (!peek(parseInfo)) { + if (!yieldEOFToken) { + break; + } + // end of stream ignore \\ - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } break; } - buffer += value + next(parseInfo); + next(parseInfo); break; case TokenMap.SINGLE_QUOTE: case TokenMap.DOUBLE_QUOTE: - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); - buffer = ""; + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } result.push(...consumeString(parseInfo)); @@ -954,32 +1152,30 @@ export function tokenize(parseInfo: ParseInfo | string, yieldEOFToken: boolean = .charAt(parseInfo.currentPosition - parseInfo.offset + 1) .charCodeAt(0); - if (!isDigit(codepoint) && buffer !== "") { - result.push(yieldResult(buffer, parseInfo)); - buffer = next(parseInfo, 2); + if (!isDigit(codepoint) && parseInfo.position !== parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); + next(parseInfo, 2); break; } - buffer += next(parseInfo); + next(parseInfo); break; default: - buffer += next(parseInfo); + next(parseInfo); break; } - if (!yieldEOFToken && endPosition <= parseInfo.stream.length - parseInfo.currentPosition + parseInfo.offset) { + if (!yieldEOFToken && endPosition <= parseInfo.currentPosition - parseInfo.offset + 1) { break; } } if (yieldEOFToken) { - if (buffer.length > 0) { - result.push(yieldResult(buffer, parseInfo)); + if (parseInfo.position < parseInfo.currentPosition) { + result.push(yieldResult(parseInfo)); } - result.push(yieldResult("", parseInfo, EnumToken.EOFTokenType)); - } else { - parseInfo.buffer = buffer; + result.push(yieldResult(parseInfo, EnumToken.EOFTokenType)); } parseInfo.time += performance.now() - startTime; @@ -998,6 +1194,8 @@ export async function* tokenizeStream( const decoder = new TextDecoder("utf-8"); const reader = input.getReader(); + parseInfo.stream = ""; + while (true) { const { done, value } = await reader.read(); const stream = ArrayBuffer.isView(value) ? decoder.decode(value, { stream: true }) : value; @@ -1005,14 +1203,11 @@ export async function* tokenizeStream( if (!done) { parseInfo.source.append(stream as string); - if (typeof parseInfo.stream != "string") { - parseInfo.stream = stream as string; - } else { - parseInfo.stream = (parseInfo.stream.slice(parseInfo.currentPosition - parseInfo.offset) + - stream) as string; - } + parseInfo.stream = (parseInfo.stream.slice(parseInfo.position - parseInfo.offset) + stream) as string; - parseInfo.offset = parseInfo.currentPosition; + parseInfo.offset = parseInfo.offset = parseInfo.position; + } else { + parseInfo.stream = ""; } yield* tokenize(parseInfo, done); diff --git a/src/lib/parser/utils/selector.ts b/src/lib/parser/utils/selector.ts index 61a7bdbf..27ba815b 100644 --- a/src/lib/parser/utils/selector.ts +++ b/src/lib/parser/utils/selector.ts @@ -39,6 +39,7 @@ import { ValidationSyntaxGroupEnum, ValidationTokenEnum } from "../../validation import type { ValidationPropertyToken } from "../../validation/parser/types.d.ts"; import { splitTokenList } from "../../validation/utils/list.ts"; import { trimWhiteSpace } from "../parse.ts"; +import { equalsIgnoreCase } from "./text.ts"; /** * parse selector @@ -373,6 +374,41 @@ export function parseSelector( func.val == ":nth-of-type" || func.val == ":nth-last-of-type" ) { + + const list: Token[] = []; + let index: number; + + for ( index = 0; index < func.chi.length; index++) { + + if (func.chi[index].typ == EnumToken.CommentTokenType || func.chi[index].typ == EnumToken.WhitespaceTokenType) { + continue; + } + + if (func.chi[index].typ == EnumToken.IdenTokenType && equalsIgnoreCase('of', (func.chi[index] as IdentToken).val)) { + + index--; + break; + } + + list.push(func.chi[index]); + } + + if (list.length == 3) { + + if (list[0].typ == EnumToken.IdenTokenType && ('n' == (list[0] as IdentToken).val || '-n' == (list[0] as IdentToken).val || '+n' == (list[0] as IdentToken).val)) { + + if (list[1].typ == EnumToken.NextSiblingCombinatorTokenType) { + + if (list[2].typ == EnumToken.NumberTokenType && (0 == (list[2] as NumberToken).val)) { + + (list[0] as IdentToken).val = 'n'; + func.chi.splice(0, index, list[0]); + break; + } + } + } + } + const token = func.chi.find( (t) => t.typ != EnumToken.WhitespaceTokenType && t.typ != EnumToken.CommentTokenType, diff --git a/src/lib/syntax/syntax.ts b/src/lib/syntax/syntax.ts index 45098b17..8d73f86b 100644 --- a/src/lib/syntax/syntax.ts +++ b/src/lib/syntax/syntax.ts @@ -1342,6 +1342,10 @@ export const isIdent = memoize(function (name: string): boolean { } if (codepoint == REVERSE_SOLIDUS) { + if (i + 1 > j) { + return false; + } + codepoint = name.charCodeAt(i + 1) as number; // if (!isIdentCodepoint(codepoint)) { @@ -1365,6 +1369,7 @@ export const isIdent = memoize(function (name: string): boolean { if (codepoint == REVERSE_SOLIDUS) { i += codepoint < 0x80 ? 1 : String.fromCodePoint(codepoint).length; codepoint = name.charCodeAt(i) as number; + i += codepoint < 0x80 ? 1 : String.fromCodePoint(codepoint).length; continue; diff --git a/src/lib/validation/match.ts b/src/lib/validation/match.ts index 16a9879f..9bf8fe58 100644 --- a/src/lib/validation/match.ts +++ b/src/lib/validation/match.ts @@ -1871,6 +1871,7 @@ function matchSyntax( }; case ValidationTokenEnum.FunctionDefinition: + if ( equalsIgnoreCase( (token as FunctionToken).val, diff --git a/src/node.ts b/src/node.ts index 471d6958..26d58168 100644 --- a/src/node.ts +++ b/src/node.ts @@ -386,7 +386,18 @@ export function transformSync( } options ??= {}; - options = { minify: true, removeEmpty: true, removeCharset: true, ...options }; + + if (options.minify == null) { + options.minify = true; + } + + if (options.removeEmpty == null) { + options.removeEmpty = true; + } + + if (options.removeCharset == null) { + options.removeCharset = true; + } const startTime: number = performance.now(); const parseResult: ParseResult = parseSync(stream, options); @@ -879,7 +890,18 @@ export async function transform( } options ??= {}; - options = { minify: true, removeEmpty: true, removeCharset: true, ...options }; + + if (options.minify == null) { + options.minify = true; + } + + if (options.removeEmpty == null) { + options.removeEmpty = true; + } + + if (options.removeCharset == null) { + options.removeCharset = true; + } const startTime: number = performance.now(); return parse(stream, options).then((parseResult: ParseResult) => { diff --git a/test/inspect.js b/test/inspect.js index ba49247c..bd928715 100644 --- a/test/inspect.js +++ b/test/inspect.js @@ -1,7 +1,9 @@ -import {dirname} from "node:path"; -import {transformFile} from '../dist/node.js'; +import { dirname } from "node:path"; +import { transform } from "../dist/node.js"; -const {code, stats} = await transformFile(dirname(new URL(import.meta.url).pathname) + '/files/css/tailwind.css'); +const { code, stats } = await transform({ + file: dirname(new URL(import.meta.url).pathname) + "/files/css/tailwind.css", +}); console.debug(code); -console.debug({stats}); \ No newline at end of file +console.debug({ stats }); From eb7dce7d140ba97cbef66dc3218d19b9e6c59354 Mon Sep 17 00:00:00 2001 From: Thierry Bela Nanga Date: Thu, 20 Aug 2026 15:56:02 -0400 Subject: [PATCH 19/22] add missing file #146 --- README.md | 2 +- dist/index-umd-web.js | 76 +++++-------- dist/index.cjs | 76 +++++-------- dist/index.d.ts | 54 +++++----- dist/lib/ast/node.js | 50 +++++++++ dist/lib/parser/parse.js | 5 - dist/lib/parser/tokenize.js | 16 --- files/ast.md | 2 +- files/index.md | 2 + files/minification.md | 196 ---------------------------------- files/prefix-removal.md | 205 ++++++++++++++++++++++++++++++++++++ files/syntax-lowering.md | 2 +- src/lib/ast/node.ts | 120 +++++++++++++++++++++ 13 files changed, 462 insertions(+), 344 deletions(-) create mode 100644 dist/lib/ast/node.js create mode 100644 files/prefix-removal.md create mode 100644 src/lib/ast/node.ts diff --git a/README.md b/README.md index ac470844..90f4c5ca 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ Try it [online](https://tbela99.github.io/css-parser/playground/) - [Syntax Lowering](https://tbela99.github.io/css-parser/docs/documents/Guide.Syntax_Lowering.html) - [Ast Manipulation](https://tbela99.github.io/css-parser/docs/documents/Guide.Ast_Manipulation.html) - [Utility Functions](https://tbela99.github.io/css-parser/docs/documents/Guide.Utility_Functions.html) -- + ## AST ### Comment diff --git a/dist/index-umd-web.js b/dist/index-umd-web.js index c711e7b8..e03e31cf 100644 --- a/dist/index-umd-web.js +++ b/dist/index-umd-web.js @@ -22090,12 +22090,6 @@ let val = parseInfo.stream.slice(parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset); let token = null; let dimension; - // if (val === "" && hint != EnumToken.EOFTokenType) { - // console.error(parseInfo.stream.length > parseInfo.currentPosition - parseInfo.offset, parseInfo.position < parseInfo.currentPosition, parseInfo.currentPosition - parseInfo.offset, parseInfo.stream.slice(parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset), parseInfo.stream.length, parseInfo.currentPosition- parseInfo.offset, parseInfo.position- parseInfo.offset, parseInfo.position, parseInfo.currentPosition, parseInfo.offset) - // console.error(new Error(`val is empty '${hint}'`)); - // } - // console.error({val, hint, position: parseInfo.position - parseInfo.offset, currentPosition: parseInfo.currentPosition - parseInfo.offset, endPosition: parseInfo.stream.length, offset: parseInfo.offset, len: parseInfo.stream.length}); - // console.error(new Error('incomplete token')); if (options?.decodeSegments) { val = val.replace(/\\([0-9a-fA-F]{1,6})(?:\s)?/g, (_, sequence) => { const codepoint = parseInt(sequence, 16); @@ -22307,9 +22301,6 @@ if ((nextCodepoint = parseInfo.stream.charCodeAt(i + 1)) != nextCodepoint) { return false; } - // if (nextCodepoint == REVERSE_SOLIDUS) { - // return name.length > 2 && !isNewLine(name.charCodeAt(2) as number); - // } if (isDigit(nextCodepoint)) { return false; } @@ -22551,7 +22542,6 @@ break; case 59 /* TokenMap.SEMICOLON */: if (parseInfo.position < parseInfo.currentPosition) { - // console.error(parseInfo.stream.length > parseInfo.currentPosition - parseInfo.offset, parseInfo.position < parseInfo.currentPosition, parseInfo.currentPosition - parseInfo.offset, endPosition, parseInfo.stream.slice(parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset), parseInfo.stream.length) result.push(yieldResult(parseInfo)); } next(parseInfo); @@ -22559,7 +22549,6 @@ break; case 58 /* TokenMap.COLON */: if (parseInfo.position < parseInfo.currentPosition) { - // console.error(parseInfo.currentPosition - parseInfo.offset, parseInfo.position - parseInfo.offset, parseInfo.stream.length, parseInfo.stream.slice(parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset)); result.push(yieldResult(parseInfo)); } next(parseInfo); @@ -22757,7 +22746,6 @@ if (!yieldEOFToken && parseInfo.stream.length == parseInfo.currentPosition - parseInfo.offset + 1) { break; } - // console.error('reverse solidus', parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset, parseInfo.stream.length, parseInfo.stream.slice(parseInfo.position, parseInfo.currentPosition)); next(parseInfo); // EOF if (!peek(parseInfo)) { @@ -22799,15 +22787,11 @@ } } if (yieldEOFToken) { - // console.error(parseInfo.stream.length > parseInfo.currentPosition - parseInfo.offset, parseInfo.position < parseInfo.currentPosition, parseInfo.currentPosition - parseInfo.offset, parseInfo.position - parseInfo.offset, parseInfo.stream.length); if (parseInfo.position < parseInfo.currentPosition) { result.push(yieldResult(parseInfo)); } result.push(yieldResult(parseInfo, exports.EnumToken.EOFTokenType)); } - // else { - // // parseInfo.buffer = buffer; - // } parseInfo.time += performance.now() - startTime; return result; } @@ -30088,7 +30072,6 @@ options.sourcesMap.set(source.id, source); const parseInfo = { stream, - buffer: "", offset: 0, source, position: 0, @@ -30550,7 +30533,6 @@ : result; const root = await doParse(stream instanceof ReadableStream ? tokenizeStream(stream, { - buffer: "", offset: 0, source: new SourceFile("", [], src.relative), position: 0, @@ -30558,7 +30540,6 @@ }) : tokenize({ stream, - buffer: "", offset: 0, position: 0, source: new SourceFile(stream, [], src.relative), @@ -31865,7 +31846,6 @@ const stream = `.x{${declaration}}`; return doParse(tokenize({ stream, - buffer: "", offset: 0, position: 0, source: new SourceFile(stream, [], ""), @@ -31899,7 +31879,6 @@ function parseString(src, options = { parseColor: true }, errors) { const parseInfo = { stream: src, - buffer: "", offset: 0, time: 0, source: new SourceFile(src, [], ""), @@ -32200,48 +32179,49 @@ } /** - * set node property + * * @param node - * @param property - * @param value + * @param key + * @returns */ - function setNodeProperty(node, property, value) { - switch (property) { + function getNodeProperty(node, key) { + switch (key) { + case "parent": + return node[PARENT]; case "location": - node[LOC] = value; - break; + return node[LOC]; case "state": - node[STATE] = value; - break; + return node[STATE]; case "errors": - node[ERRORS] = value; - break; + return node[ERRORS]; case "tokens": - node[TOKENS] = value; - break; - case "parent": - node[PARENT] = value; - break; + return node[TOKENS]; } + return undefined; } /** - * get node property + * * @param node - * @param property - * @returns + * @param key + * @param value */ - function getNodeProperty(node, property) { - switch (property) { + function setNodeProperty(node, key, value) { + switch (key) { + case "parent": + node[PARENT] = value; + break; case "location": - return node[LOC]; + node[LOC] = value; + break; case "state": - return node[STATE]; + node[STATE] = value; + break; case "errors": - return node[ERRORS]; + node[ERRORS] = value; + break; case "tokens": - return node[TOKENS]; - case "parent": - return node[PARENT]; + node[TOKENS] = value; + break; } } diff --git a/dist/index.cjs b/dist/index.cjs index d7f70875..090ea035 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -22093,12 +22093,6 @@ function yieldResult(parseInfo, hint, options) { let val = parseInfo.stream.slice(parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset); let token = null; let dimension; - // if (val === "" && hint != EnumToken.EOFTokenType) { - // console.error(parseInfo.stream.length > parseInfo.currentPosition - parseInfo.offset, parseInfo.position < parseInfo.currentPosition, parseInfo.currentPosition - parseInfo.offset, parseInfo.stream.slice(parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset), parseInfo.stream.length, parseInfo.currentPosition- parseInfo.offset, parseInfo.position- parseInfo.offset, parseInfo.position, parseInfo.currentPosition, parseInfo.offset) - // console.error(new Error(`val is empty '${hint}'`)); - // } - // console.error({val, hint, position: parseInfo.position - parseInfo.offset, currentPosition: parseInfo.currentPosition - parseInfo.offset, endPosition: parseInfo.stream.length, offset: parseInfo.offset, len: parseInfo.stream.length}); - // console.error(new Error('incomplete token')); if (options?.decodeSegments) { val = val.replace(/\\([0-9a-fA-F]{1,6})(?:\s)?/g, (_, sequence) => { const codepoint = parseInt(sequence, 16); @@ -22310,9 +22304,6 @@ function isIdentToken(parseInfo, start, end) { if ((nextCodepoint = parseInfo.stream.charCodeAt(i + 1)) != nextCodepoint) { return false; } - // if (nextCodepoint == REVERSE_SOLIDUS) { - // return name.length > 2 && !isNewLine(name.charCodeAt(2) as number); - // } if (isDigit(nextCodepoint)) { return false; } @@ -22554,7 +22545,6 @@ function tokenize(parseInfo, yieldEOFToken = true) { break; case 59 /* TokenMap.SEMICOLON */: if (parseInfo.position < parseInfo.currentPosition) { - // console.error(parseInfo.stream.length > parseInfo.currentPosition - parseInfo.offset, parseInfo.position < parseInfo.currentPosition, parseInfo.currentPosition - parseInfo.offset, endPosition, parseInfo.stream.slice(parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset), parseInfo.stream.length) result.push(yieldResult(parseInfo)); } next(parseInfo); @@ -22562,7 +22552,6 @@ function tokenize(parseInfo, yieldEOFToken = true) { break; case 58 /* TokenMap.COLON */: if (parseInfo.position < parseInfo.currentPosition) { - // console.error(parseInfo.currentPosition - parseInfo.offset, parseInfo.position - parseInfo.offset, parseInfo.stream.length, parseInfo.stream.slice(parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset)); result.push(yieldResult(parseInfo)); } next(parseInfo); @@ -22760,7 +22749,6 @@ function tokenize(parseInfo, yieldEOFToken = true) { if (!yieldEOFToken && parseInfo.stream.length == parseInfo.currentPosition - parseInfo.offset + 1) { break; } - // console.error('reverse solidus', parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset, parseInfo.stream.length, parseInfo.stream.slice(parseInfo.position, parseInfo.currentPosition)); next(parseInfo); // EOF if (!peek(parseInfo)) { @@ -22802,15 +22790,11 @@ function tokenize(parseInfo, yieldEOFToken = true) { } } if (yieldEOFToken) { - // console.error(parseInfo.stream.length > parseInfo.currentPosition - parseInfo.offset, parseInfo.position < parseInfo.currentPosition, parseInfo.currentPosition - parseInfo.offset, parseInfo.position - parseInfo.offset, parseInfo.stream.length); if (parseInfo.position < parseInfo.currentPosition) { result.push(yieldResult(parseInfo)); } result.push(yieldResult(parseInfo, exports.EnumToken.EOFTokenType)); } - // else { - // // parseInfo.buffer = buffer; - // } parseInfo.time += performance.now() - startTime; return result; } @@ -30091,7 +30075,6 @@ async function doParse(iter, options = {}) { options.sourcesMap.set(source.id, source); const parseInfo = { stream, - buffer: "", offset: 0, source, position: 0, @@ -30553,7 +30536,6 @@ async function doParse(iter, options = {}) { : result; const root = await doParse(stream instanceof ReadableStream ? tokenizeStream(stream, { - buffer: "", offset: 0, source: new SourceFile("", [], src.relative), position: 0, @@ -30561,7 +30543,6 @@ async function doParse(iter, options = {}) { }) : tokenize({ stream, - buffer: "", offset: 0, position: 0, source: new SourceFile(stream, [], src.relative), @@ -31868,7 +31849,6 @@ async function parseDeclarations(declaration) { const stream = `.x{${declaration}}`; return doParse(tokenize({ stream, - buffer: "", offset: 0, position: 0, source: new SourceFile(stream, [], ""), @@ -31902,7 +31882,6 @@ async function parseDeclarations(declaration) { function parseString(src, options = { parseColor: true }, errors) { const parseInfo = { stream: src, - buffer: "", offset: 0, time: 0, source: new SourceFile(src, [], ""), @@ -32203,48 +32182,49 @@ function validateSyncArguments(options, prefix = "options.") { } /** - * set node property + * * @param node - * @param property - * @param value + * @param key + * @returns */ -function setNodeProperty(node, property, value) { - switch (property) { +function getNodeProperty(node, key) { + switch (key) { + case "parent": + return node[PARENT]; case "location": - node[LOC] = value; - break; + return node[LOC]; case "state": - node[STATE] = value; - break; + return node[STATE]; case "errors": - node[ERRORS] = value; - break; + return node[ERRORS]; case "tokens": - node[TOKENS] = value; - break; - case "parent": - node[PARENT] = value; - break; + return node[TOKENS]; } + return undefined; } /** - * get node property + * * @param node - * @param property - * @returns + * @param key + * @param value */ -function getNodeProperty(node, property) { - switch (property) { +function setNodeProperty(node, key, value) { + switch (key) { + case "parent": + node[PARENT] = value; + break; case "location": - return node[LOC]; + node[LOC] = value; + break; case "state": - return node[STATE]; + node[STATE] = value; + break; case "errors": - return node[ERRORS]; + node[ERRORS] = value; + break; case "tokens": - return node[TOKENS]; - case "parent": - return node[PARENT]; + node[TOKENS] = value; + break; } } diff --git a/dist/index.d.ts b/dist/index.d.ts index 0228c38e..e918a136 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -4137,7 +4137,6 @@ declare class SourceFile { } export declare interface PropertyListOptions { - removeDuplicateDeclarations?: boolean | string | string[]; computeShorthand?: boolean; } @@ -4146,7 +4145,6 @@ export declare interface PropertyListOptions { * parse info */ export declare interface ParseInfo$1 { - /** * stream */ @@ -4156,7 +4154,7 @@ export declare interface ParseInfo$1 { * Source file */ source: SourceFile; - + /** * last token position */ @@ -6475,68 +6473,68 @@ declare function replaceNodeOrValue(parent: BinaryExpressionToken | (AstNode$1 & /** * * @param node - * @param property - * @param value + * @param key */ -declare function setNodeProperty(node: AstNode$1, property: "location", value: SourceLocation): void; +declare function getNodeProperty(node: AstNode$1, key: 'parent'): AstNode$1 | Token$1 | null; /** * * @param node - * @param property - * @param value + * @param key */ -declare function setNodeProperty(node: AstNode$1, property: "state", value: EnumAstNodeStatus$1): void; +declare function getNodeProperty(node: AstNode$1, key: 'location'): SourceLocation | null; /** * * @param node - * @param property - * @param value + * @param key */ -declare function setNodeProperty(node: AstNode$1, property: "errors", value: ErrorDescription$1[]): void; +declare function getNodeProperty(node: AstNode$1, key: 'state'): EnumAstNodeStatus$1 | null; /** * * @param node - * @param property - * @param value + * @param key */ -declare function setNodeProperty(node: AstNode$1, property: "tokens", value: Token$1[]): void; +declare function getNodeProperty(node: AstNode$1, key: 'errors'): ErrorDescription$1[] | null; /** * * @param node - * @param property - * @param value + * @param key */ -declare function setNodeProperty(node: AstNode$1, property: "parent", value: AstNode$1 | Token$1): void; +declare function getNodeProperty(node: AstNode$1, key: 'tokens'): Token$1[] | null; /** * * @param node - * @param property + * @param key + * @param value */ -declare function getNodeProperty(node: AstNode$1, property: "location"): SourceLocation | null; +declare function setNodeProperty(node: AstNode$1, key: 'parent', value: AstNode$1 | Token$1 | null): void; /** * * @param node - * @param property + * @param key + * @param value */ -declare function getNodeProperty(node: AstNode$1, property: "state"): EnumAstNodeStatus$1 | null; +declare function setNodeProperty(node: AstNode$1, key: 'location', value: SourceLocation | null): void; /** * * @param node - * @param property + * @param key + * @param value */ -declare function getNodeProperty(node: AstNode$1, property: "errors"): ErrorDescription$1[] | null; +declare function setNodeProperty(node: AstNode$1, key: 'state', value: EnumAstNodeStatus$1 | null): void; /** * * @param node - * @param property + * @param key + * @param value */ -declare function getNodeProperty(node: AstNode$1, property: "tokens"): Token$1[] | null; +declare function setNodeProperty(node: AstNode$1, key: 'errors', value: ErrorDescription$1[] | null): void; /** * * @param node - * @param property + * @param key + * @param value */ -declare function getNodeProperty(node: AstNode$1, property: "parent"): AstNode$1 | Token$1 | null; +declare function setNodeProperty(node: AstNode$1, key: 'tokens', value: Token$1[] | null): void; /** * Load file or url diff --git a/dist/lib/ast/node.js b/dist/lib/ast/node.js new file mode 100644 index 00000000..134448da --- /dev/null +++ b/dist/lib/ast/node.js @@ -0,0 +1,50 @@ +import { TOKENS, ERRORS, STATE, LOC, PARENT } from '../syntax/constants.js'; + +/** + * + * @param node + * @param key + * @returns + */ +function getNodeProperty(node, key) { + switch (key) { + case "parent": + return node[PARENT]; + case "location": + return node[LOC]; + case "state": + return node[STATE]; + case "errors": + return node[ERRORS]; + case "tokens": + return node[TOKENS]; + } + return undefined; +} +/** + * + * @param node + * @param key + * @param value + */ +function setNodeProperty(node, key, value) { + switch (key) { + case "parent": + node[PARENT] = value; + break; + case "location": + node[LOC] = value; + break; + case "state": + node[STATE] = value; + break; + case "errors": + node[ERRORS] = value; + break; + case "tokens": + node[TOKENS] = value; + break; + } +} + +export { getNodeProperty, setNodeProperty }; diff --git a/dist/lib/parser/parse.js b/dist/lib/parser/parse.js index 2ddee828..95d367d8 100644 --- a/dist/lib/parser/parse.js +++ b/dist/lib/parser/parse.js @@ -1500,7 +1500,6 @@ async function doParse(iter, options = {}) { options.sourcesMap.set(source.id, source); const parseInfo = { stream, - buffer: "", offset: 0, source, position: 0, @@ -1962,7 +1961,6 @@ async function doParse(iter, options = {}) { : result; const root = await doParse(stream instanceof ReadableStream ? tokenizeStream(stream, { - buffer: "", offset: 0, source: new SourceFile("", [], src.relative), position: 0, @@ -1970,7 +1968,6 @@ async function doParse(iter, options = {}) { }) : tokenize({ stream, - buffer: "", offset: 0, position: 0, source: new SourceFile(stream, [], src.relative), @@ -3277,7 +3274,6 @@ async function parseDeclarations(declaration) { const stream = `.x{${declaration}}`; return doParse(tokenize({ stream, - buffer: "", offset: 0, position: 0, source: new SourceFile(stream, [], ""), @@ -3311,7 +3307,6 @@ async function parseDeclarations(declaration) { function parseString(src, options = { parseColor: true }, errors) { const parseInfo = { stream: src, - buffer: "", offset: 0, time: 0, source: new SourceFile(src, [], ""), diff --git a/dist/lib/parser/tokenize.js b/dist/lib/parser/tokenize.js index cadfb3bb..a2154bc2 100644 --- a/dist/lib/parser/tokenize.js +++ b/dist/lib/parser/tokenize.js @@ -211,12 +211,6 @@ function yieldResult(parseInfo, hint, options) { let val = parseInfo.stream.slice(parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset); let token = null; let dimension; - // if (val === "" && hint != EnumToken.EOFTokenType) { - // console.error(parseInfo.stream.length > parseInfo.currentPosition - parseInfo.offset, parseInfo.position < parseInfo.currentPosition, parseInfo.currentPosition - parseInfo.offset, parseInfo.stream.slice(parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset), parseInfo.stream.length, parseInfo.currentPosition- parseInfo.offset, parseInfo.position- parseInfo.offset, parseInfo.position, parseInfo.currentPosition, parseInfo.offset) - // console.error(new Error(`val is empty '${hint}'`)); - // } - // console.error({val, hint, position: parseInfo.position - parseInfo.offset, currentPosition: parseInfo.currentPosition - parseInfo.offset, endPosition: parseInfo.stream.length, offset: parseInfo.offset, len: parseInfo.stream.length}); - // console.error(new Error('incomplete token')); if (options?.decodeSegments) { val = val.replace(/\\([0-9a-fA-F]{1,6})(?:\s)?/g, (_, sequence) => { const codepoint = parseInt(sequence, 16); @@ -428,9 +422,6 @@ function isIdentToken(parseInfo, start, end) { if ((nextCodepoint = parseInfo.stream.charCodeAt(i + 1)) != nextCodepoint) { return false; } - // if (nextCodepoint == REVERSE_SOLIDUS) { - // return name.length > 2 && !isNewLine(name.charCodeAt(2) as number); - // } if (isDigit(nextCodepoint)) { return false; } @@ -672,7 +663,6 @@ function tokenize(parseInfo, yieldEOFToken = true) { break; case 59 /* TokenMap.SEMICOLON */: if (parseInfo.position < parseInfo.currentPosition) { - // console.error(parseInfo.stream.length > parseInfo.currentPosition - parseInfo.offset, parseInfo.position < parseInfo.currentPosition, parseInfo.currentPosition - parseInfo.offset, endPosition, parseInfo.stream.slice(parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset), parseInfo.stream.length) result.push(yieldResult(parseInfo)); } next(parseInfo); @@ -680,7 +670,6 @@ function tokenize(parseInfo, yieldEOFToken = true) { break; case 58 /* TokenMap.COLON */: if (parseInfo.position < parseInfo.currentPosition) { - // console.error(parseInfo.currentPosition - parseInfo.offset, parseInfo.position - parseInfo.offset, parseInfo.stream.length, parseInfo.stream.slice(parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset)); result.push(yieldResult(parseInfo)); } next(parseInfo); @@ -878,7 +867,6 @@ function tokenize(parseInfo, yieldEOFToken = true) { if (!yieldEOFToken && parseInfo.stream.length == parseInfo.currentPosition - parseInfo.offset + 1) { break; } - // console.error('reverse solidus', parseInfo.position - parseInfo.offset, parseInfo.currentPosition - parseInfo.offset, parseInfo.stream.length, parseInfo.stream.slice(parseInfo.position, parseInfo.currentPosition)); next(parseInfo); // EOF if (!peek(parseInfo)) { @@ -920,15 +908,11 @@ function tokenize(parseInfo, yieldEOFToken = true) { } } if (yieldEOFToken) { - // console.error(parseInfo.stream.length > parseInfo.currentPosition - parseInfo.offset, parseInfo.position < parseInfo.currentPosition, parseInfo.currentPosition - parseInfo.offset, parseInfo.position - parseInfo.offset, parseInfo.stream.length); if (parseInfo.position < parseInfo.currentPosition) { result.push(yieldResult(parseInfo)); } result.push(yieldResult(parseInfo, EnumToken.EOFTokenType)); } - // else { - // // parseInfo.buffer = buffer; - // } parseInfo.time += performance.now() - startTime; return result; } diff --git a/files/ast.md b/files/ast.md index d57df1a0..7e9197f0 100644 --- a/files/ast.md +++ b/files/ast.md @@ -416,4 +416,4 @@ button { ``` ------ -[← Syntax Lowering](./syntax-lowering.md) | [Utility Functions →](./utilities.md) \ No newline at end of file +[← Prefix Removal](./prefix-removal.md) | [Utility Functions →](./utilities.md) \ No newline at end of file diff --git a/files/index.md b/files/index.md index 91b6094b..42e3493e 100644 --- a/files/index.md +++ b/files/index.md @@ -12,6 +12,7 @@ children: - ./sourcemap.md - ./plugins.md - ./syntax-lowering.md + - ./prefix-removal.md - ./ast.md - ./utilities.md --- @@ -27,6 +28,7 @@ children: - [Sourcemap](./sourcemap.md) - [Plugins API](./plugins.md) - [Syntax Lowering](./syntax-lowering.md) +- [Prefix Removal](./prefix-removal.md) - [Ast Manipulation](./ast.md) - [Utility Functions](./utilities.md) diff --git a/files/minification.md b/files/minification.md index 56a418c6..2d0c7dc0 100644 --- a/files/minification.md +++ b/files/minification.md @@ -657,202 +657,6 @@ Output: } ``` -### CSS prefix removal - -This feature is disabled by default. - -```ts - -import {transform} from '@tbela99/css-parser'; - -const css = ` - -::-webkit-input-placeholder { - color: gray; - } - - ::-moz-placeholder { - color: gray; - } - - :-ms-input-placeholder { - color: gray; - } - - ::-ms-input-placeholder { - color: gray; - } - - ::placeholder { - color: gray; - } - - @supports selector(:-ms-input-placeholder) { - - - :-ms-input-placeholder { - color: gray; - } - } - -@media (-webkit-min-device-pixel-ratio: 2), (-o-min-device-pixel-ratio: 2/1), (min-resolution: 2dppx) { - .image { - background-image: url(image@2x.png); - } - - } - - - @-webkit-keyframes bar { - - from, 0% { - - height: 10px; - } - } - - @keyframes bar { - - from, 0% { - - height: 10px; - } - } - .example { - - -moz-animation: bar 1s infinite; - display: -ms-grid; - display: grid; - -webkit-transition: all .5s; - -o-transition: all .5s; - transition: all .5s; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - background: -o-linear-gradient(top, white, black); - background: -webkit-gradient(linear, left top, left bottom, from(white), to(black)); - background: linear-gradient(to bottom, white, black); - } - - .site{ - display:-ms-grid; - display:grid; -ms-grid-columns:2fr 1fr; - grid-template-columns:2fr 1fr; - grid-template-areas:"header header" - "title sidebar" - "main sidebar" - "footer footer"; - } - .site > *{padding:30px; color:#fff; font-size:20px;} - .mastheader{ - -ms-grid-row:1; - -ms-grid-column:1; - -ms-grid-column-span:2; - grid-area:header; - } - .page-title{ - -ms-grid-row:2; - -ms-grid-column:1; - grid-area:title; - } - .main-content{ - -ms-grid-row:3; - -ms-grid-column:1; - grid-area:main; - } - .sidebar{ - -ms-grid-row:2; - -ms-grid-row-span:2; - -ms-grid-column:2; - grid-area:sidebar; - } - .footer{ - -ms-grid-row:4; - -ms-grid-column:1; - -ms-grid-column-span:2; - grid-area:footer; - } -`; -const result = await transform(css, { - - beautify: true, - removePrefix: true - } -); - -console.log(result.code); -``` - -Output: - -```css -::placeholder { - color: grey -} -@supports selector(::placeholder) { - ::placeholder { - color: grey - } -} -@media (min-resolution:2x) { - .image { - background-image: url(image@2x.png) - } -} -@keyframes bar { - 0% { - height: 10px - } -} -.site,.example { - display: grid -} -.site { - grid-template-columns: 2fr 1fr; - grid-template-areas: "header header""title sidebar""main sidebar""footer footer" -} -.example { - animation: bar 1s infinite; - transition: .5s; - user-select: none; - background: linear-gradient(#fff,#000) -} -.site>* { - padding: 30px; - color: #fff; - font-size: 20px -} -.mastheader { - grid-row: 1; - grid-column: 1; - grid-column-end: 2; - grid-area: header -} -.page-title { - grid-row: 2; - grid-column: 1; - grid-area: title -} -.main-content { - grid-row: 3; - grid-column: 1; - grid-area: main -} -.sidebar { - grid-row: 2; - grid-row-end: 2; - grid-column: 2; - grid-area: sidebar -} -.footer { - grid-row: 4; - grid-column: 1; - grid-column-end: 2; - grid-area: footer -} -``` - ### Shorthands Shorthand properties are computed and default values are removed. diff --git a/files/prefix-removal.md b/files/prefix-removal.md new file mode 100644 index 00000000..d88aac49 --- /dev/null +++ b/files/prefix-removal.md @@ -0,0 +1,205 @@ +--- +title: Prefix Removal +group: Documents +category: Guides +--- + +## Prefix Removal + +Vendor prefixes can be removed by enabling the `removePrefix` flag. + +```ts + +import {transformSync} from '@tbela99/css-parser'; + +const css = ` + +::-webkit-input-placeholder { + color: gray; + } + + ::-moz-placeholder { + color: gray; + } + + :-ms-input-placeholder { + color: gray; + } + + ::-ms-input-placeholder { + color: gray; + } + + ::placeholder { + color: gray; + } + + @supports selector(:-ms-input-placeholder) { + + + :-ms-input-placeholder { + color: gray; + } + } + +@media (-webkit-min-device-pixel-ratio: 2), (-o-min-device-pixel-ratio: 2/1), (min-resolution: 2dppx) { + .image { + background-image: url(image@2x.png); + } + + } + + + @-webkit-keyframes bar { + + from, 0% { + + height: 10px; + } + } + + @keyframes bar { + + from, 0% { + + height: 10px; + } + } + .example { + + -moz-animation: bar 1s infinite; + display: -ms-grid; + display: grid; + -webkit-transition: all .5s; + -o-transition: all .5s; + transition: all .5s; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background: -o-linear-gradient(top, white, black); + background: -webkit-gradient(linear, left top, left bottom, from(white), to(black)); + background: linear-gradient(to bottom, white, black); + } + + .site{ + display:-ms-grid; + display:grid; + -ms-grid-columns:2fr 1fr; + grid-template-columns:2fr 1fr; + grid-template-areas:"header header" + "title sidebar" + "main sidebar" + "footer footer"; + } + .site > *{padding:30px; color:#fff; font-size:20px;} + .mastheader{ + -ms-grid-row:1; + -ms-grid-column:1; + -ms-grid-column-span:2; + grid-area:header; + } + .page-title{ + -ms-grid-row:2; + -ms-grid-column:1; + grid-area:title; + } + .main-content{ + -ms-grid-row:3; + -ms-grid-column:1; + grid-area:main; + } + .sidebar{ + -ms-grid-row:2; + -ms-grid-row-span:2; + -ms-grid-column:2; + grid-area:sidebar; + } + .footer{ + -ms-grid-row:4; + -ms-grid-column:1; + -ms-grid-column-span:2; + grid-area:footer; + } +`; +const result = await transformSync(css, { + + beautify: true, + removePrefix: true + } +); + +console.log(result.code); +``` + +Output: + +```css +::placeholder { + color: grey +} +@supports selector(::placeholder) { + ::placeholder { + color: grey + } +} +@media (min-resolution:2x) { + .image { + background-image: url(image@2x.png) + } +} +@keyframes bar { + 0% { + height: 10px + } +} +.site,.example { + display: grid +} +.site { + grid-template-columns: 2fr 1fr; + grid-template-areas: "header header""title sidebar""main sidebar""footer footer" +} +.example { + animation: bar 1s infinite; + transition: .5s; + user-select: none; + background: linear-gradient(#fff,#000) +} +.site>* { + padding: 30px; + color: #fff; + font-size: 20px +} +.mastheader { + grid-row: 1; + grid-column: 1; + grid-column-end: 2; + grid-area: header +} +.page-title { + grid-row: 2; + grid-column: 1; + grid-area: title +} +.main-content { + grid-row: 3; + grid-column: 1; + grid-area: main +} +.sidebar { + grid-row: 2; + grid-row-end: 2; + grid-column: 2; + grid-area: sidebar +} +.footer { + grid-row: 4; + grid-column: 1; + grid-column-end: 2; + grid-area: footer +} +``` + +------ +[← Syntax Lowering](./syntax-lowering.md) | [Ast Manipulation →](./ast.md) \ No newline at end of file diff --git a/files/syntax-lowering.md b/files/syntax-lowering.md index e13fe447..e85b7f7d 100644 --- a/files/syntax-lowering.md +++ b/files/syntax-lowering.md @@ -135,4 +135,4 @@ table.colortable th { ```` ------ -[← Plugins API](./plugins.md) | [Ast Manipulation →](./ast.md) \ No newline at end of file +[← Plugins API](./plugins.md) | [Prefix Removal →](./prefix-removal.md) \ No newline at end of file diff --git a/src/lib/ast/node.ts b/src/lib/ast/node.ts new file mode 100644 index 00000000..7d98c19c --- /dev/null +++ b/src/lib/ast/node.ts @@ -0,0 +1,120 @@ +import type { AstNode, ErrorDescription, SourceLocation, Token } from "../../@types/index.d.ts"; +import { ERRORS, LOC, PARENT, STATE, TOKENS } from "../syntax/constants.ts"; +import { AstNodePropertyType, EnumAstNodeStatus } from "./types.ts"; + +/** + * + * @param node + * @param key + */ +export function getNodeProperty(node: AstNode, key: 'parent'): AstNode | Token | null; +/** + * + * @param node + * @param key + */ +export function getNodeProperty(node: AstNode, key: 'location'): SourceLocation | null; +/** + * + * @param node + * @param key + */ +export function getNodeProperty(node: AstNode, key: 'state'): EnumAstNodeStatus | null; +/** + * + * @param node + * @param key + */ +export function getNodeProperty(node: AstNode, key: 'errors'): ErrorDescription[] | null; +/** + * + * @param node + * @param key + */ +export function getNodeProperty(node: AstNode, key: 'tokens'): Token[] | null; + +/** + * + * @param node + * @param key + * @returns + */ +export function getNodeProperty(node: AstNode, key: AstNodePropertyType): any { + + switch (key) { + + case "parent": + return node[PARENT]; + case "location": + return node[LOC]; + case "state": + return node[STATE]; + case "errors": + return node[ERRORS]; + case "tokens": + return node[TOKENS]; + } + return undefined; +} + +/** + * + * @param node + * @param key + * @param value + */ +export function setNodeProperty(node: AstNode, key: 'parent', value: AstNode | Token | null): void; +/** + * + * @param node + * @param key + * @param value + */ +export function setNodeProperty(node: AstNode, key: 'location', value: SourceLocation | null): void; +/** + * + * @param node + * @param key + * @param value + */ +export function setNodeProperty(node: AstNode, key: 'state', value: EnumAstNodeStatus | null): void; +/** + * + * @param node + * @param key + * @param value + */ +export function setNodeProperty(node: AstNode, key: 'errors', value: ErrorDescription[] | null): void; +/** + * + * @param node + * @param key + * @param value + */ +export function setNodeProperty(node: AstNode, key: 'tokens', value: Token[] | null): void; + +/** + * + * @param node + * @param key + * @param value + */ +export function setNodeProperty(node: AstNode, key: AstNodePropertyType, value: any): void { + switch (key) { + case "parent": + node[PARENT] = value; + break; + case "location": + node[LOC] = value; + break; + case "state": + node[STATE] = value; + break; + case "errors": + node[ERRORS] = value; + break; + case "tokens": + node[TOKENS] = value; + break; + } +} \ No newline at end of file From 293266f7988b7f629f2f722a1c2751af571d02ab Mon Sep 17 00:00:00 2001 From: Thierry Bela Nanga Date: Thu, 20 Aug 2026 15:59:55 -0400 Subject: [PATCH 20/22] add missing file #146 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 90f4c5ca..660514be 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,7 @@ Try it [online](https://tbela99.github.io/css-parser/playground/) - [Sourcemap](https://tbela99.github.io/css-parser/docs/documents/Guide.Sourcemap.html) - [Plugins API](https://tbela99.github.io/css-parser/docs/documents/Guide.Plugins_API.html) - [Syntax Lowering](https://tbela99.github.io/css-parser/docs/documents/Guide.Syntax_Lowering.html) +- [Prefix Removal](https://tbela99.github.io/css-parser/docs/documents/Guide.Prefix_Removal.html) - [Ast Manipulation](https://tbela99.github.io/css-parser/docs/documents/Guide.Ast_Manipulation.html) - [Utility Functions](https://tbela99.github.io/css-parser/docs/documents/Guide.Utility_Functions.html) From ea813358d9fe24c63067dfd61a82a81ea3f2bdf9 Mon Sep 17 00:00:00 2001 From: Thierry Bela Date: Thu, 20 Aug 2026 18:08:54 -0400 Subject: [PATCH 21/22] fix resolve paths on windows #146 --- src/lib/fs/resolve.ts | 3 ++- test/specs/code/block.js | 15 ++++++++------- test/specs/code/import1.js | 3 ++- test/specs/code/modules.js | 8 +++++--- test/specs/code/sourcemaps.js | 8 +++++--- test/specs/code/validation.js | 16 +++++++++------- 6 files changed, 31 insertions(+), 22 deletions(-) diff --git a/src/lib/fs/resolve.ts b/src/lib/fs/resolve.ts index 057e0874..f863f743 100644 --- a/src/lib/fs/resolve.ts +++ b/src/lib/fs/resolve.ts @@ -181,7 +181,8 @@ export const resolve = memoize(function ( } const dir = cwd || currentDirectory; - const absolute = dir == "" || url.startsWith("/") ? resolvePath(url) : resolvePath(dir, url); + const absolute = + dir == "" || url.startsWith("/") || url.match(/^[a-zA-Z]:/) ? resolvePath(url) : resolvePath(dir, url); return { absolute, diff --git a/test/specs/code/block.js b/test/specs/code/block.js index 5250c7bb..dc02514c 100644 --- a/test/specs/code/block.js +++ b/test/specs/code/block.js @@ -1,4 +1,6 @@ export function run(describe, expect, it, transform, parse, render, dirname, readFile, resolve) { + const root = new URL(dirname(import.meta.url) + "/../../../"); + describe("doParse block", function () { it("similar rules #1", function () { const file = ` @@ -1136,12 +1138,12 @@ font-family: random-item(--x, {Times, serif}, {Arial, sans-serif}, {Courier, mon it("stream file #50", async () => { // const dir = resolve((import.meta.dirname ?? dirname(new URL(import.meta.url).pathname)) + "/../..").absolute; // const file = `@import '${dir}/files/css/line-awesome.css`; - + const url = new URL(import.meta.url); - url.pathname = dirname(url.pathname) + "/../../files/css/bootstrap-4.css"; - + url.pathname = dirname(url.pathname) + "/../../files/css/bootstrap-4.css"; + const options = { - file: url.pathname , + file: url.pathname.replace(root.pathname, ""), beautify: true, }; @@ -1153,12 +1155,11 @@ font-family: random-item(--x, {Times, serif}, {Arial, sans-serif}, {Courier, mon }); it("stream file #51", async () => { - const url = new URL(import.meta.url); - url.pathname = dirname(url.pathname) + "/../../files/css/tailwind.css"; + url.pathname = dirname(url.pathname) + "/../../files/css/tailwind.css"; const options = { - file: url.pathname, + file: url.pathname.replace(root.pathname, ""), beautify: true, }; diff --git a/test/specs/code/import1.js b/test/specs/code/import1.js index 485d7bd6..c97434e6 100644 --- a/test/specs/code/import1.js +++ b/test/specs/code/import1.js @@ -1,9 +1,10 @@ export function run(describe, expect, it, transform, parse, render, dirname) { + const root = new URL(dirname(import.meta.url) + '/../../../'); const url = new URL(dirname(import.meta.url) + '/../../files/css/color.css?v=1'); const atRule = ` -@import '${url.pathname}'; +@import '${url.pathname.replace(root.pathname, '')}'; abbr[title], abbr[data-original-title] { text-decoration: underline dotted; -webkit-text-decoration: underline dotted; diff --git a/test/specs/code/modules.js b/test/specs/code/modules.js index 9498898b..68a05349 100644 --- a/test/specs/code/modules.js +++ b/test/specs/code/modules.js @@ -17,6 +17,8 @@ export function run( transformSync, parseSync, ) { + const root = new URL(dirname(import.meta.url) + "/../../../"); + describe("css modules", function () { it("module #1", function () { return transform( @@ -125,7 +127,7 @@ export function run( .indigo-white { composes: bg-indigo; -composes: button cell title from "${url.pathname}"; color: white; +composes: button cell title from "${url.pathname.replace(root.pathname, "")}"; color: white; } `, { @@ -645,7 +647,7 @@ a span { .indigo-white { composes: bg-indigo; - composes: button cell title from "${url.pathname}"; color: white; + composes: button cell title from "${url.pathname.replace(root.pathname, "")}"; color: white; } `, { @@ -743,7 +745,7 @@ a span { ` /* import your colors... */ - @value colors: "${url.pathname}"; + @value colors: "${url.pathname.replace(root.pathname, "")}"; @value blue, red, green from colors; .button { diff --git a/test/specs/code/sourcemaps.js b/test/specs/code/sourcemaps.js index 939a2bdc..7a4f3038 100644 --- a/test/specs/code/sourcemaps.js +++ b/test/specs/code/sourcemaps.js @@ -17,12 +17,14 @@ export function run( transformSync, parseSync, ) { + const root = new URL(dirname(import.meta.url) + "/../../../"); + describe("sourcemap", function () { const url = new URL(dirname(import.meta.url) + "/../../files/css/nested.css"); // const file = `@import '${dir}/files/css/line-awesome.css`; const options = { input: ` -@import '${url.pathname}'; +@import '${url.pathname.replace(root.pathname, "")}'; h1 { text-transform: uppercase; } @@ -62,10 +64,10 @@ button { // result2.map.computePositions(); let positions = result2.map.find(1, 254); expect(positions?.[0]?.slice?.(0, 3)).deep.equals([null, 19, 2]); - + positions = result2.map.find(1, 255); expect(positions).equals(null); - + positions = result2.map.find(100, 255); expect(positions).equals(null); }); diff --git a/test/specs/code/validation.js b/test/specs/code/validation.js index 8124a458..ead711fc 100644 --- a/test/specs/code/validation.js +++ b/test/specs/code/validation.js @@ -1,5 +1,7 @@ export function run(describe, expect, it, transform, parse, render, dirname, readFile) { + const root = new URL(dirname(import.meta.url) + '/../../../'); + describe('selector validation', function () { it('selector validation #1', function () { @@ -506,14 +508,14 @@ html, body, div, span, applet, object, iframe, it('file validation #21', function () { const url = new URL(dirname(import.meta.url) + '/../../files/css/full.css'); - return transform(`@import '${url.pathname}'; + return transform(`@import '${url.pathname.replace(root.pathname, '')}'; `, {validation: true, resolveImport: true}).then(result => expect(result.errors.length).equals(5)); }); it('file validation #22', function () { const url = new URL(dirname(import.meta.url) + '/../../files/css/bootstrap.css'); - transform(`@import '${url.pathname}'; + transform(`@import '${url.pathname.replace(root.pathname, '')}'; `, { validation: true, resolveImport: true @@ -523,7 +525,7 @@ html, body, div, span, applet, object, iframe, it('file validation #23', function () { const url = new URL(dirname(import.meta.url) + '/../../files/css/bootstrap-4.css'); - return transform(`@import '${url.pathname}'; + return transform(`@import '${url.pathname.replace(root.pathname, '')}'; `, { validation: true, resolveImport: true @@ -533,7 +535,7 @@ html, body, div, span, applet, object, iframe, it('file validation #24', function () { const url = new URL(dirname(import.meta.url) + '/../../files/css/bootstrap-5.css'); - return transform(`@import '${url.pathname}'; + return transform(`@import '${url.pathname.replace(root.pathname, '')}'; `, { validation: true, resolveImport: true @@ -543,7 +545,7 @@ html, body, div, span, applet, object, iframe, it('file validation #25', function () { const url = new URL(dirname(import.meta.url) + '/../../files/css/tailwind.css'); - return transform(`@import '${url.pathname}'; + return transform(`@import '${url.pathname.replace(root.pathname, '')}'; `, { validation: true, resolveImport: true @@ -554,7 +556,7 @@ html, body, div, span, applet, object, iframe, const url = new URL(dirname(import.meta.url) + '/../../files/css/tailwind-2.0.4.css'); - return transform(`@import '${url.pathname}'; + return transform(`@import '${url.pathname.replace(root.pathname, '')}'; `, { validation: true, resolveImport: true @@ -565,7 +567,7 @@ html, body, div, span, applet, object, iframe, const url = new URL(dirname(import.meta.url) + '/../../files/css/github-markdown.css'); - return transform(`@import '${url.pathname}'; + return transform(`@import '${url.pathname.replace(root.pathname, '')}'; `, { validation: true, resolveImport: true From 60db68f3f104a890ee9e90f0b4ddde10b811683d Mon Sep 17 00:00:00 2001 From: Thierry Bela Date: Thu, 20 Aug 2026 18:42:09 -0400 Subject: [PATCH 22/22] remove extra space #146 --- dist/index-umd-web.js | 9 +++++++-- dist/index.cjs | 9 +++++++-- dist/lib/fs/resolve.js | 2 +- dist/lib/parser/tokenize.js | 7 ++++++- src/lib/parser/tokenize.ts | 6 +++++- 5 files changed, 26 insertions(+), 7 deletions(-) diff --git a/dist/index-umd-web.js b/dist/index-umd-web.js index e03e31cf..08381ca2 100644 --- a/dist/index-umd-web.js +++ b/dist/index-umd-web.js @@ -22155,7 +22155,12 @@ else { let slice = val.slice(1); const chr = val.charAt(0); - if (chr == "@" && isIdent(slice)) { + if (chr == "!" && equalsIgnoreCase("!important", val)) { + token = { + typ: exports.EnumToken.ImportantTokenType, + }; + } + else if (chr == "@" && isIdent(slice)) { token = { typ: exports.EnumToken.AtRuleTokenType, nam: slice, @@ -24680,7 +24685,7 @@ currentDirectory = normalize(currentDirectory); } const dir = cwd || currentDirectory; - const absolute = dir == "" || url.startsWith("/") ? resolvePath(url) : resolvePath(dir, url); + const absolute = dir == "" || url.startsWith("/") || url.match(/^[a-zA-Z]:/) ? resolvePath(url) : resolvePath(dir, url); return { absolute, relative: dir === "" ? absolute : diff(absolute, dir), diff --git a/dist/index.cjs b/dist/index.cjs index 090ea035..fceaa1b3 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -22158,7 +22158,12 @@ function yieldResult(parseInfo, hint, options) { else { let slice = val.slice(1); const chr = val.charAt(0); - if (chr == "@" && isIdent(slice)) { + if (chr == "!" && equalsIgnoreCase("!important", val)) { + token = { + typ: exports.EnumToken.ImportantTokenType, + }; + } + else if (chr == "@" && isIdent(slice)) { token = { typ: exports.EnumToken.AtRuleTokenType, nam: slice, @@ -24683,7 +24688,7 @@ const resolve = memoize(function (url, currentDirectory, cwd) { currentDirectory = normalize(currentDirectory); } const dir = cwd || currentDirectory; - const absolute = dir == "" || url.startsWith("/") ? resolvePath(url) : resolvePath(dir, url); + const absolute = dir == "" || url.startsWith("/") || url.match(/^[a-zA-Z]:/) ? resolvePath(url) : resolvePath(dir, url); return { absolute, relative: dir === "" ? absolute : diff(absolute, dir), diff --git a/dist/lib/fs/resolve.js b/dist/lib/fs/resolve.js index 51da577d..f1f9255a 100644 --- a/dist/lib/fs/resolve.js +++ b/dist/lib/fs/resolve.js @@ -149,7 +149,7 @@ const resolve = memoize(function (url, currentDirectory, cwd) { currentDirectory = normalize(currentDirectory); } const dir = cwd || currentDirectory; - const absolute = dir == "" || url.startsWith("/") ? resolvePath(url) : resolvePath(dir, url); + const absolute = dir == "" || url.startsWith("/") || url.match(/^[a-zA-Z]:/) ? resolvePath(url) : resolvePath(dir, url); return { absolute, relative: dir === "" ? absolute : diff(absolute, dir), diff --git a/dist/lib/parser/tokenize.js b/dist/lib/parser/tokenize.js index a2154bc2..efd932cb 100644 --- a/dist/lib/parser/tokenize.js +++ b/dist/lib/parser/tokenize.js @@ -276,7 +276,12 @@ function yieldResult(parseInfo, hint, options) { else { let slice = val.slice(1); const chr = val.charAt(0); - if (chr == "@" && isIdent(slice)) { + if (chr == "!" && equalsIgnoreCase("!important", val)) { + token = { + typ: EnumToken.ImportantTokenType, + }; + } + else if (chr == "@" && isIdent(slice)) { token = { typ: EnumToken.AtRuleTokenType, nam: slice, diff --git a/src/lib/parser/tokenize.ts b/src/lib/parser/tokenize.ts index ec34e16c..e4a6e4dc 100644 --- a/src/lib/parser/tokenize.ts +++ b/src/lib/parser/tokenize.ts @@ -377,7 +377,11 @@ export function yieldResult( let slice: string = val.slice(1); const chr: string = val.charAt(0); - if (chr == "@" && isIdent(slice)) { + if (chr == "!" && equalsIgnoreCase("!important", val)) { + token = { + typ: EnumToken.ImportantTokenType, + } as Token; + } else if (chr == "@" && isIdent(slice)) { token = { typ: EnumToken.AtRuleTokenType, nam: slice,