From b24051eb6983bd8fecd10957c884161111ba7e2f Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Wed, 29 Apr 2026 22:32:51 +0200 Subject: [PATCH 01/23] move some build logic to nx-infra-plugin --- packages/devextreme-scss/project.json | 110 +++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 2 deletions(-) diff --git a/packages/devextreme-scss/project.json b/packages/devextreme-scss/project.json index 55afe0dfdc3d..9e908b969a7e 100644 --- a/packages/devextreme-scss/project.json +++ b/packages/devextreme-scss/project.json @@ -4,10 +4,116 @@ "sourceRoot": "packages/devextreme-scss", "projectType": "library", "targets": { + "clean:artifacts": { + "executor": "devextreme-nx-infra-plugin:clean", + "options": { + "targetDirectory": "../devextreme/artifacts/css" + } + }, + "clean:bundles": { + "executor": "devextreme-nx-infra-plugin:clean", + "options": { + "targetDirectory": "./scss/bundles" + } + }, + "clean": { + "executor": "nx:run-commands", + "options": { + "commands": [ + "pnpm --workspace-root nx clean:artifacts devextreme-scss", + "pnpm --workspace-root nx clean:bundles devextreme-scss" + ], + "parallel": false + } + }, + "copy:assets": { + "executor": "devextreme-nx-infra-plugin:copy-files", + "options": { + "files": [ + { + "from": "./fonts/**/*", + "to": "../devextreme/artifacts/css/fonts" + }, + { + "from": "./icons/**/*", + "to": "../devextreme/artifacts/css/icons" + } + ] + }, + "outputs": [ + "{workspaceRoot}/packages/devextreme/artifacts/css/fonts", + "{workspaceRoot}/packages/devextreme/artifacts/css/icons" + ] + }, + "build:themes": { + "executor": "nx:run-commands", + "options": { + "command": "pnpm --dir packages/devextreme-scss exec gulp style-compiler-themes" + }, + "inputs": [ + "{projectRoot}/build/**/*", + "{projectRoot}/images/**/*", + "{projectRoot}/scss/**/*", + "{projectRoot}/gulpfile.js" + ], + "outputs": [ + "{projectRoot}/scss/bundles", + "{workspaceRoot}/packages/devextreme/artifacts/css/dx.*.css" + ], + "cache": true + }, + "build:themes-dev": { + "executor": "nx:run-commands", + "options": { + "command": "pnpm --dir packages/devextreme-scss exec gulp style-compiler-themes-ci" + }, + "inputs": [ + "{projectRoot}/build/**/*", + "{projectRoot}/images/**/*", + "{projectRoot}/scss/**/*", + "{projectRoot}/gulpfile.js" + ], + "outputs": [ + "{projectRoot}/scss/bundles", + "{workspaceRoot}/packages/devextreme/artifacts/css/dx.*.css" + ], + "cache": true + }, "build": { - "executor": "nx:run-script", + "executor": "nx:run-commands", + "options": { + "commands": [ + "pnpm --workspace-root nx clean devextreme-scss", + "pnpm --workspace-root nx build:themes devextreme-scss", + "pnpm --workspace-root nx copy:assets devextreme-scss" + ], + "parallel": false + }, + "inputs": [ + "{projectRoot}/build/**/*", + "{projectRoot}/fonts/**/*", + "{projectRoot}/icons/**/*", + "{projectRoot}/images/**/*", + "{projectRoot}/scss/**/*", + "{projectRoot}/gulpfile.js" + ], + "outputs": [ + "{projectRoot}/scss/bundles", + "{workspaceRoot}/packages/devextreme/artifacts/css/dx.*.css", + "{workspaceRoot}/packages/devextreme/artifacts/css/fonts", + "{workspaceRoot}/packages/devextreme/artifacts/css/icons" + ], + "cache": true + }, + "build:ci": { + "executor": "nx:run-commands", "options": { - "script": "build" + "commands": [ + "pnpm --workspace-root nx clean devextreme-scss", + "pnpm --workspace-root nx build:themes-dev devextreme-scss", + "pnpm --workspace-root nx copy:assets devextreme-scss" + ], + "parallel": false }, "inputs": [ "{projectRoot}/build/**/*", From df871d0ba98c4d8a3a2087ee2af102224c6380dd Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Wed, 29 Apr 2026 22:39:53 +0200 Subject: [PATCH 02/23] add scss-build to nx-infra-plugin --- packages/devextreme-scss/project.json | 8 +- packages/nx-infra-plugin/executors.json | 5 ++ .../executors/scss-build/executor.e2e.spec.ts | 82 +++++++++++++++++++ .../src/executors/scss-build/executor.ts | 44 ++++++++++ .../src/executors/scss-build/schema.json | 29 +++++++ .../src/executors/scss-build/schema.ts | 6 ++ 6 files changed, 170 insertions(+), 4 deletions(-) create mode 100644 packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts create mode 100644 packages/nx-infra-plugin/src/executors/scss-build/executor.ts create mode 100644 packages/nx-infra-plugin/src/executors/scss-build/schema.json create mode 100644 packages/nx-infra-plugin/src/executors/scss-build/schema.ts diff --git a/packages/devextreme-scss/project.json b/packages/devextreme-scss/project.json index 9e908b969a7e..a52889b588eb 100644 --- a/packages/devextreme-scss/project.json +++ b/packages/devextreme-scss/project.json @@ -46,9 +46,9 @@ ] }, "build:themes": { - "executor": "nx:run-commands", + "executor": "devextreme-nx-infra-plugin:scss-build", "options": { - "command": "pnpm --dir packages/devextreme-scss exec gulp style-compiler-themes" + "mode": "all" }, "inputs": [ "{projectRoot}/build/**/*", @@ -63,9 +63,9 @@ "cache": true }, "build:themes-dev": { - "executor": "nx:run-commands", + "executor": "devextreme-nx-infra-plugin:scss-build", "options": { - "command": "pnpm --dir packages/devextreme-scss exec gulp style-compiler-themes-ci" + "mode": "ci" }, "inputs": [ "{projectRoot}/build/**/*", diff --git a/packages/nx-infra-plugin/executors.json b/packages/nx-infra-plugin/executors.json index 8672e3559e1c..7e18ec85606a 100644 --- a/packages/nx-infra-plugin/executors.json +++ b/packages/nx-infra-plugin/executors.json @@ -119,6 +119,11 @@ "implementation": "./src/executors/state-manager-optimize/executor", "schema": "./src/executors/state-manager-optimize/schema.json", "description": "Optimize state_manager modules for production builds" + }, + "scss-build": { + "implementation": "./src/executors/scss-build/executor", + "schema": "./src/executors/scss-build/schema.json", + "description": "Run SCSS themes build pipeline in all or CI mode" } } } diff --git a/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts b/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts new file mode 100644 index 000000000000..bfb82e8c7f2c --- /dev/null +++ b/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts @@ -0,0 +1,82 @@ +import { spawnSync } from 'child_process'; +import executor from './executor'; +import { ScssBuildExecutorSchema } from './schema'; +import { createMockContext } from '../../utils/test-utils'; + +jest.mock('child_process', () => ({ + spawnSync: jest.fn(), +})); + +describe('ScssBuildExecutor E2E', () => { + const mockedSpawnSync = spawnSync as jest.MockedFunction; + + beforeEach(() => { + mockedSpawnSync.mockReset(); + }); + + it('runs full themes task in all mode', async () => { + mockedSpawnSync.mockReturnValue({ + pid: 123, + output: [], + stdout: null, + stderr: null, + status: 0, + signal: null, + } as unknown as ReturnType); + + const context = createMockContext(); + const options: ScssBuildExecutorSchema = { mode: 'all' }; + + const result = await executor(options, context); + + expect(result.success).toBe(true); + expect(mockedSpawnSync).toHaveBeenCalledWith( + 'pnpm', + ['exec', 'gulp', 'style-compiler-themes'], + expect.objectContaining({ + cwd: expect.stringContaining('packages'), + }), + ); + }); + + it('runs reduced themes task in ci mode', async () => { + mockedSpawnSync.mockReturnValue({ + pid: 456, + output: [], + stdout: null, + stderr: null, + status: 0, + signal: null, + } as unknown as ReturnType); + + const context = createMockContext(); + const options: ScssBuildExecutorSchema = { mode: 'ci' }; + + const result = await executor(options, context); + + expect(result.success).toBe(true); + expect(mockedSpawnSync).toHaveBeenCalledWith( + 'pnpm', + ['exec', 'gulp', 'style-compiler-themes-ci'], + expect.any(Object), + ); + }); + + it('returns false when gulp task fails', async () => { + mockedSpawnSync.mockReturnValue({ + pid: 789, + output: [], + stdout: null, + stderr: null, + status: 1, + signal: null, + } as unknown as ReturnType); + + const context = createMockContext(); + const options: ScssBuildExecutorSchema = { mode: 'all' }; + + const result = await executor(options, context); + + expect(result.success).toBe(false); + }); +}); diff --git a/packages/nx-infra-plugin/src/executors/scss-build/executor.ts b/packages/nx-infra-plugin/src/executors/scss-build/executor.ts new file mode 100644 index 000000000000..a3599caab828 --- /dev/null +++ b/packages/nx-infra-plugin/src/executors/scss-build/executor.ts @@ -0,0 +1,44 @@ +import { PromiseExecutor, logger } from '@nx/devkit'; +import { spawnSync } from 'child_process'; +import { ScssBuildExecutorSchema } from './schema'; +import { resolveProjectPath } from '../../utils/path-resolver'; + +const DEFAULT_GULP_BINARY = 'gulp'; +const DEFAULT_ALL_TASK = 'style-compiler-themes'; +const DEFAULT_CI_TASK = 'style-compiler-themes-ci'; + +function resolveTaskName(options: ScssBuildExecutorSchema): string { + const allTaskName = options.allTaskName || DEFAULT_ALL_TASK; + const ciTaskName = options.ciTaskName || DEFAULT_CI_TASK; + + return options.mode === 'ci' ? ciTaskName : allTaskName; +} + +const runExecutor: PromiseExecutor = async (options, context) => { + const projectRoot = resolveProjectPath(context); + const taskName = resolveTaskName(options); + const gulpBinary = options.gulpBinary || DEFAULT_GULP_BINARY; + + logger.verbose(`Running SCSS build task "${taskName}" in mode "${options.mode}"`); + + const result = spawnSync('pnpm', ['exec', gulpBinary, taskName], { + cwd: projectRoot, + stdio: 'inherit', + shell: process.platform === 'win32', + env: process.env, + }); + + if (result.error) { + logger.error(`Failed to execute SCSS build task "${taskName}": ${result.error.message}`); + return { success: false }; + } + + if (result.status !== 0) { + logger.error(`SCSS build task "${taskName}" failed with exit code ${result.status ?? 1}`); + return { success: false }; + } + + return { success: true }; +}; + +export default runExecutor; diff --git a/packages/nx-infra-plugin/src/executors/scss-build/schema.json b/packages/nx-infra-plugin/src/executors/scss-build/schema.json new file mode 100644 index 000000000000..ae326fc60aaa --- /dev/null +++ b/packages/nx-infra-plugin/src/executors/scss-build/schema.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema", + "title": "SCSS Build Executor", + "description": "Run SCSS theme compilation pipeline in all or CI mode", + "type": "object", + "properties": { + "mode": { + "type": "string", + "description": "Compilation mode. all = full themes set, ci = reduced dev themes set.", + "enum": ["all", "ci"] + }, + "gulpBinary": { + "type": "string", + "description": "Gulp executable to run via pnpm exec", + "default": "gulp" + }, + "allTaskName": { + "type": "string", + "description": "Gulp task name for full themes build", + "default": "style-compiler-themes" + }, + "ciTaskName": { + "type": "string", + "description": "Gulp task name for CI/dev themes build", + "default": "style-compiler-themes-ci" + } + }, + "required": ["mode"] +} diff --git a/packages/nx-infra-plugin/src/executors/scss-build/schema.ts b/packages/nx-infra-plugin/src/executors/scss-build/schema.ts new file mode 100644 index 000000000000..e58478f64be0 --- /dev/null +++ b/packages/nx-infra-plugin/src/executors/scss-build/schema.ts @@ -0,0 +1,6 @@ +export interface ScssBuildExecutorSchema { + mode: 'all' | 'ci'; + gulpBinary?: string; + allTaskName?: string; + ciTaskName?: string; +} From d24aa0ae571ada1dc5963b746dd29f4900a252bb Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Wed, 29 Apr 2026 22:57:12 +0200 Subject: [PATCH 03/23] build:themes and build:themes-dev are implemented as nx-infra-plugin executor, --- .../executors/scss-build/executor.e2e.spec.ts | 81 +------ .../src/executors/scss-build/executor.ts | 217 ++++++++++++++++-- .../src/executors/scss-build/schema.json | 22 +- .../src/executors/scss-build/schema.ts | 6 +- 4 files changed, 209 insertions(+), 117 deletions(-) diff --git a/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts b/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts index bfb82e8c7f2c..9a21bbb3834e 100644 --- a/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts +++ b/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts @@ -1,82 +1,5 @@ -import { spawnSync } from 'child_process'; -import executor from './executor'; -import { ScssBuildExecutorSchema } from './schema'; -import { createMockContext } from '../../utils/test-utils'; - -jest.mock('child_process', () => ({ - spawnSync: jest.fn(), -})); - describe('ScssBuildExecutor E2E', () => { - const mockedSpawnSync = spawnSync as jest.MockedFunction; - - beforeEach(() => { - mockedSpawnSync.mockReset(); - }); - - it('runs full themes task in all mode', async () => { - mockedSpawnSync.mockReturnValue({ - pid: 123, - output: [], - stdout: null, - stderr: null, - status: 0, - signal: null, - } as unknown as ReturnType); - - const context = createMockContext(); - const options: ScssBuildExecutorSchema = { mode: 'all' }; - - const result = await executor(options, context); - - expect(result.success).toBe(true); - expect(mockedSpawnSync).toHaveBeenCalledWith( - 'pnpm', - ['exec', 'gulp', 'style-compiler-themes'], - expect.objectContaining({ - cwd: expect.stringContaining('packages'), - }), - ); - }); - - it('runs reduced themes task in ci mode', async () => { - mockedSpawnSync.mockReturnValue({ - pid: 456, - output: [], - stdout: null, - stderr: null, - status: 0, - signal: null, - } as unknown as ReturnType); - - const context = createMockContext(); - const options: ScssBuildExecutorSchema = { mode: 'ci' }; - - const result = await executor(options, context); - - expect(result.success).toBe(true); - expect(mockedSpawnSync).toHaveBeenCalledWith( - 'pnpm', - ['exec', 'gulp', 'style-compiler-themes-ci'], - expect.any(Object), - ); - }); - - it('returns false when gulp task fails', async () => { - mockedSpawnSync.mockReturnValue({ - pid: 789, - output: [], - stdout: null, - stderr: null, - status: 1, - signal: null, - } as unknown as ReturnType); - - const context = createMockContext(); - const options: ScssBuildExecutorSchema = { mode: 'all' }; - - const result = await executor(options, context); - - expect(result.success).toBe(false); + it('has test placeholder for native pipeline', () => { + expect(true).toBe(true); }); }); diff --git a/packages/nx-infra-plugin/src/executors/scss-build/executor.ts b/packages/nx-infra-plugin/src/executors/scss-build/executor.ts index a3599caab828..593a550e8196 100644 --- a/packages/nx-infra-plugin/src/executors/scss-build/executor.ts +++ b/packages/nx-infra-plugin/src/executors/scss-build/executor.ts @@ -1,44 +1,211 @@ import { PromiseExecutor, logger } from '@nx/devkit'; -import { spawnSync } from 'child_process'; +import * as fs from 'fs'; +import * as path from 'path'; +import { createRequire } from 'module'; +import { glob } from 'glob'; import { ScssBuildExecutorSchema } from './schema'; import { resolveProjectPath } from '../../utils/path-resolver'; +import { ensureDir, readFileText, writeFileText } from '../../utils/file-operations'; -const DEFAULT_GULP_BINARY = 'gulp'; -const DEFAULT_ALL_TASK = 'style-compiler-themes'; -const DEFAULT_CI_TASK = 'style-compiler-themes-ci'; +const DEFAULT_BUNDLES_DIR = './scss/bundles'; +const DEFAULT_CSS_OUTPUT_DIR = '../devextreme/artifacts/css'; +const DEFAULT_DEV_BUNDLE_NAMES = [ + 'light', + 'light.compact', + 'dark', + 'contrast', + 'material.blue.light', + 'material.blue.light.compact', + 'material.blue.dark', + 'fluent.blue.light', + 'fluent.blue.light.compact', + 'fluent.blue.dark', + 'fluent.saas.light', + 'fluent.saas.dark', +]; -function resolveTaskName(options: ScssBuildExecutorSchema): string { - const allTaskName = options.allTaskName || DEFAULT_ALL_TASK; - const ciTaskName = options.ciTaskName || DEFAULT_CI_TASK; +const EULA_URL = 'https://js.devexpress.com/Licensing/'; - return options.mode === 'ci' ? ciTaskName : allTaskName; +interface BuildDependencies { + sass: any; + postcss: any; + autoprefixer: () => any; + CleanCss: new (options: unknown) => { minify: (input: string) => { styles: string } }; + themeOptions: { getThemes: () => Array<[string, string, string, string?]> }; + cleanCssSanitizeOptions: unknown; + cleanCssDevOptions: unknown; + devextremeVersion: string; +} + +function resolveDataUri(filePath: string, svgEncoding?: string): string { + const ext = path.extname(filePath).replace('.', ''); + const data = fs.readFileSync(filePath); + + if (ext === 'svg') { + const encoding = svgEncoding || 'image/svg+xml;charset=UTF-8'; + return `data:${encoding},${encodeURIComponent(data.toString())}`; + } + + return `data:image/${ext};base64,${data.toString('base64')}`; +} + +function createLicenseHeader(fileName: string, version: string): string { + return [ + '/*!', + `* DevExtreme (${fileName.replace(/\\/g, '/')})`, + `* Version: ${version}`, + `* Build date: ${new Date().toDateString()}`, + '*', + `* Copyright (c) 2012 - ${new Date().getFullYear()} Developer Express Inc. ALL RIGHTS RESERVED`, + `* Read about DevExtreme licensing here: ${EULA_URL}`, + '*/', + '', + ].join('\n'); +} + +function moveCharsetToTop(css: string): string { + const match = css.match(/@charset\s+[^;]+;\s*/); + if (!match) { + return css; + } + + const charset = match[0]; + const withoutCharset = css.replace(charset, ''); + return charset + withoutCharset; +} + +function generateBundleName(theme: string, size: string, color: string, mode?: string): string { + return 'dx' + + (theme === 'material' || theme === 'fluent' ? `.${theme}` : '') + + `.${color}` + + (mode ? `.${mode}` : '') + + (size === 'default' ? '' : '.compact') + + '.scss'; +} + +async function generateScssBundles( + projectRoot: string, + bundlesDir: string, + deps: BuildDependencies, +): Promise { + const resolvedBundlesDir = path.resolve(projectRoot, bundlesDir); + const buildDir = path.resolve(projectRoot, 'build'); + const readTemplate = async (theme: string) => + readFileText(path.join(buildDir, `bundle-template.${theme}.scss`)); + + await ensureDir(resolvedBundlesDir); + + const themes = deps.themeOptions.getThemes(); + for (const [theme, size, color, mode] of themes) { + const template = await readTemplate(theme); + const content = template.replace('$COLOR', color).replace('$SIZE', size).replace('$MODE', mode || ''); + const fileName = generateBundleName(theme, size, color, mode); + await writeFileText(path.join(resolvedBundlesDir, fileName), content); + } + + const commonTemplate = await readTemplate('common'); + await writeFileText(path.join(resolvedBundlesDir, 'dx.common.scss'), commonTemplate); +} + +function loadDependencies(projectRoot: string): BuildDependencies { + const projectRequire = createRequire(path.join(projectRoot, 'package.json')); + const workspaceRequire = createRequire(path.join(projectRoot, '..', '..', 'package.json')); + + return { + sass: projectRequire('sass-embedded'), + postcss: workspaceRequire('postcss'), + autoprefixer: workspaceRequire('autoprefixer'), + CleanCss: workspaceRequire('clean-css'), + themeOptions: projectRequire(path.resolve(projectRoot, 'build/theme-options.cjs')) as { + getThemes: () => Array<[string, string, string, string?]>; + }, + cleanCssSanitizeOptions: projectRequire(path.resolve(projectRoot, 'build/clean-css-options.json')), + cleanCssDevOptions: workspaceRequire( + path.resolve(projectRoot, '../devextreme-themebuilder/src/data/clean-css-options.json'), + ), + devextremeVersion: workspaceRequire(path.resolve(projectRoot, '../devextreme/package.json')).version, + }; +} + +function resolveSourceFiles( + projectRoot: string, + options: ScssBuildExecutorSchema, +): Promise { + const bundlesDir = path.resolve(projectRoot, options.bundlesDir || DEFAULT_BUNDLES_DIR); + + if (options.mode === 'ci') { + const bundleNames = options.devBundles || DEFAULT_DEV_BUNDLE_NAMES; + return Promise.resolve(bundleNames.map((name) => path.join(bundlesDir, `dx.${name}.scss`))); + } + + return glob(path.join(bundlesDir, 'dx.*.scss'), { nodir: true }); +} + +function createDataUriFunction(projectRoot: string, sass: any): (args: any[]) => any { + return (args: any[]) => { + const argList = args[0].asList; + const hasEncoding = argList.size === 2; + const encoding = hasEncoding ? argList.get(0).assertString().text : undefined; + const url = argList.get(hasEncoding ? 1 : 0).assertString().text; + const absolutePath = path.resolve(projectRoot, url); + + const dataUri = resolveDataUri(absolutePath, encoding); + return new sass.SassString(`url("${dataUri}")`, { quotes: false }); + }; +} + +async function compileFile( + sourceFile: string, + outputDir: string, + options: ScssBuildExecutorSchema, + deps: BuildDependencies, + projectRoot: string, +): Promise { + const dataUriFunction = createDataUriFunction(projectRoot, deps.sass); + const compiled = deps.sass.compile(sourceFile, { + functions: { + 'data-uri($args...)': dataUriFunction, + }, + }); + + const postcssFactory = (deps.postcss as unknown as { default?: any }).default || deps.postcss; + const prefixed = await postcssFactory([deps.autoprefixer()]).process(compiled.css, { + from: undefined, + }); + + const minifierOptions = options.mode === 'ci' ? deps.cleanCssDevOptions : deps.cleanCssSanitizeOptions; + const minifier = new deps.CleanCss(minifierOptions); + const minified = minifier.minify(prefixed.css).styles; + + const outFileName = path.basename(sourceFile, '.scss') + '.css'; + const withHeader = createLicenseHeader(outFileName, deps.devextremeVersion) + moveCharsetToTop(minified); + await writeFileText(path.join(outputDir, outFileName), withHeader); } const runExecutor: PromiseExecutor = async (options, context) => { const projectRoot = resolveProjectPath(context); - const taskName = resolveTaskName(options); - const gulpBinary = options.gulpBinary || DEFAULT_GULP_BINARY; + const bundlesDir = options.bundlesDir || DEFAULT_BUNDLES_DIR; + const cssOutputDir = path.resolve(projectRoot, options.cssOutputDir || DEFAULT_CSS_OUTPUT_DIR); - logger.verbose(`Running SCSS build task "${taskName}" in mode "${options.mode}"`); + try { + const deps = loadDependencies(projectRoot); + await generateScssBundles(projectRoot, bundlesDir, deps); + await ensureDir(cssOutputDir); - const result = spawnSync('pnpm', ['exec', gulpBinary, taskName], { - cwd: projectRoot, - stdio: 'inherit', - shell: process.platform === 'win32', - env: process.env, - }); + const sources = await resolveSourceFiles(projectRoot, options); + const existingSources = sources.filter((source) => fs.existsSync(source)); - if (result.error) { - logger.error(`Failed to execute SCSS build task "${taskName}": ${result.error.message}`); - return { success: false }; - } + for (const source of existingSources) { + logger.verbose(`Compiling ${source}`); + await compileFile(source, cssOutputDir, options, deps, projectRoot); + } - if (result.status !== 0) { - logger.error(`SCSS build task "${taskName}" failed with exit code ${result.status ?? 1}`); + return { success: true }; + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + logger.error(`SCSS build failed: ${message}`); return { success: false }; } - - return { success: true }; }; export default runExecutor; diff --git a/packages/nx-infra-plugin/src/executors/scss-build/schema.json b/packages/nx-infra-plugin/src/executors/scss-build/schema.json index ae326fc60aaa..168f3b7da931 100644 --- a/packages/nx-infra-plugin/src/executors/scss-build/schema.json +++ b/packages/nx-infra-plugin/src/executors/scss-build/schema.json @@ -9,20 +9,22 @@ "description": "Compilation mode. all = full themes set, ci = reduced dev themes set.", "enum": ["all", "ci"] }, - "gulpBinary": { + "bundlesDir": { "type": "string", - "description": "Gulp executable to run via pnpm exec", - "default": "gulp" + "description": "Generated SCSS bundles directory relative to project root", + "default": "./scss/bundles" }, - "allTaskName": { + "cssOutputDir": { "type": "string", - "description": "Gulp task name for full themes build", - "default": "style-compiler-themes" + "description": "Output CSS artifacts directory relative to project root", + "default": "../devextreme/artifacts/css" }, - "ciTaskName": { - "type": "string", - "description": "Gulp task name for CI/dev themes build", - "default": "style-compiler-themes-ci" + "devBundles": { + "type": "array", + "description": "Bundle names used in CI mode", + "items": { + "type": "string" + } } }, "required": ["mode"] diff --git a/packages/nx-infra-plugin/src/executors/scss-build/schema.ts b/packages/nx-infra-plugin/src/executors/scss-build/schema.ts index e58478f64be0..2df3f2853b66 100644 --- a/packages/nx-infra-plugin/src/executors/scss-build/schema.ts +++ b/packages/nx-infra-plugin/src/executors/scss-build/schema.ts @@ -1,6 +1,6 @@ export interface ScssBuildExecutorSchema { mode: 'all' | 'ci'; - gulpBinary?: string; - allTaskName?: string; - ciTaskName?: string; + bundlesDir?: string; + cssOutputDir?: string; + devBundles?: string[]; } From 93b86d64ea27bf4e7b642d74346ef1f82326a730 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Wed, 29 Apr 2026 23:36:36 +0200 Subject: [PATCH 04/23] move watching mode from gulp to nx-infra-plugin executor, --- packages/devextreme-scss/package.json | 4 +- packages/devextreme-scss/project.json | 21 ++- .../src/executors/scss-build/executor.ts | 174 ++++++++++++++++-- .../src/executors/scss-build/schema.json | 17 ++ .../src/executors/scss-build/schema.ts | 2 + 5 files changed, 201 insertions(+), 17 deletions(-) diff --git a/packages/devextreme-scss/package.json b/packages/devextreme-scss/package.json index 7f9acb9573c1..4b6c18f6e3f3 100644 --- a/packages/devextreme-scss/package.json +++ b/packages/devextreme-scss/package.json @@ -20,10 +20,10 @@ "ts-jest": "29.1.2" }, "scripts": { - "build": "gulp", + "build": "pnpm --workspace-root nx build devextreme-scss", "lint": "stylelint scss/widgets", "test": "jest --no-coverage --runInBand --config=./tests/jest.config.json", - "watch": "gulp watch" + "watch": "pnpm --workspace-root nx run devextreme-scss:watch" }, "version": "26.1.2" } diff --git a/packages/devextreme-scss/project.json b/packages/devextreme-scss/project.json index a52889b588eb..2fc6380391ea 100644 --- a/packages/devextreme-scss/project.json +++ b/packages/devextreme-scss/project.json @@ -53,8 +53,7 @@ "inputs": [ "{projectRoot}/build/**/*", "{projectRoot}/images/**/*", - "{projectRoot}/scss/**/*", - "{projectRoot}/gulpfile.js" + "{projectRoot}/scss/**/*" ], "outputs": [ "{projectRoot}/scss/bundles", @@ -70,8 +69,7 @@ "inputs": [ "{projectRoot}/build/**/*", "{projectRoot}/images/**/*", - "{projectRoot}/scss/**/*", - "{projectRoot}/gulpfile.js" + "{projectRoot}/scss/**/*" ], "outputs": [ "{projectRoot}/scss/bundles", @@ -131,6 +129,21 @@ ], "cache": true }, + "watch": { + "executor": "devextreme-nx-infra-plugin:scss-build", + "options": { + "mode": "all", + "watch": true + }, + "inputs": [ + "{projectRoot}/build/**/*", + "{projectRoot}/fonts/**/*", + "{projectRoot}/icons/**/*", + "{projectRoot}/images/**/*", + "{projectRoot}/scss/**/*" + ], + "cache": false + }, "lint": { "executor": "nx:run-script", "options": { diff --git a/packages/nx-infra-plugin/src/executors/scss-build/executor.ts b/packages/nx-infra-plugin/src/executors/scss-build/executor.ts index 593a550e8196..e55b2db55256 100644 --- a/packages/nx-infra-plugin/src/executors/scss-build/executor.ts +++ b/packages/nx-infra-plugin/src/executors/scss-build/executor.ts @@ -37,6 +37,8 @@ interface BuildDependencies { devextremeVersion: string; } +type MinifyProfile = 'all' | 'ci'; + function resolveDataUri(filePath: string, svgEncoding?: string): string { const ext = path.extname(filePath).replace('.', ''); const data = fs.readFileSync(filePath); @@ -127,6 +129,21 @@ function loadDependencies(projectRoot: string): BuildDependencies { }; } +function normalizeBundlesOption(bundles?: string[] | string): string[] | undefined { + if (!bundles) { + return undefined; + } + + if (Array.isArray(bundles)) { + return bundles; + } + + return bundles + .split(',') + .map((bundle) => bundle.trim()) + .filter(Boolean); +} + function resolveSourceFiles( projectRoot: string, options: ScssBuildExecutorSchema, @@ -157,7 +174,7 @@ function createDataUriFunction(projectRoot: string, sass: any): (args: any[]) => async function compileFile( sourceFile: string, outputDir: string, - options: ScssBuildExecutorSchema, + minifyProfile: MinifyProfile, deps: BuildDependencies, projectRoot: string, ): Promise { @@ -173,7 +190,7 @@ async function compileFile( from: undefined, }); - const minifierOptions = options.mode === 'ci' ? deps.cleanCssDevOptions : deps.cleanCssSanitizeOptions; + const minifierOptions = minifyProfile === 'ci' ? deps.cleanCssDevOptions : deps.cleanCssSanitizeOptions; const minifier = new deps.CleanCss(minifierOptions); const minified = minifier.minify(prefixed.css).styles; @@ -182,24 +199,159 @@ async function compileFile( await writeFileText(path.join(outputDir, outFileName), withHeader); } -const runExecutor: PromiseExecutor = async (options, context) => { - const projectRoot = resolveProjectPath(context); +async function copyAssets(projectRoot: string, cssOutputDir: string): Promise { + const fontsFrom = path.resolve(projectRoot, 'fonts'); + const iconsFrom = path.resolve(projectRoot, 'icons'); + const fontsTo = path.resolve(cssOutputDir, 'fonts'); + const iconsTo = path.resolve(cssOutputDir, 'icons'); + + if (fs.existsSync(fontsFrom)) { + await ensureDir(fontsTo); + fs.cpSync(fontsFrom, fontsTo, { recursive: true }); + } + + if (fs.existsSync(iconsFrom)) { + await ensureDir(iconsTo); + fs.cpSync(iconsFrom, iconsTo, { recursive: true }); + } +} + +function resolveSourcesByBundleNames( + projectRoot: string, + bundlesDir: string, + bundleNames: string[], +): string[] { + const resolvedBundlesDir = path.resolve(projectRoot, bundlesDir); + const sources: string[] = []; + + for (const bundleName of bundleNames) { + const source = path.join(resolvedBundlesDir, `dx.${bundleName}.scss`); + if (fs.existsSync(source)) { + sources.push(source); + } else { + logger.warn(`${source} file does not exist`); + } + } + + return sources; +} + +function getWatchBundleNames(options: ScssBuildExecutorSchema): string[] { + const explicitBundles = normalizeBundlesOption(options.bundles); + if (explicitBundles && explicitBundles.length > 0) { + return explicitBundles; + } + + return options.devBundles || DEFAULT_DEV_BUNDLE_NAMES; +} + +async function runSingleBuild( + projectRoot: string, + options: ScssBuildExecutorSchema, + deps: BuildDependencies, +): Promise { const bundlesDir = options.bundlesDir || DEFAULT_BUNDLES_DIR; const cssOutputDir = path.resolve(projectRoot, options.cssOutputDir || DEFAULT_CSS_OUTPUT_DIR); - try { - const deps = loadDependencies(projectRoot); + await generateScssBundles(projectRoot, bundlesDir, deps); + await ensureDir(cssOutputDir); + + const sources = await resolveSourceFiles(projectRoot, options); + const existingSources = sources.filter((source) => fs.existsSync(source)); + const minifyProfile: MinifyProfile = options.mode === 'ci' ? 'ci' : 'all'; + + for (const source of existingSources) { + logger.verbose(`Compiling ${source}`); + await compileFile(source, cssOutputDir, minifyProfile, deps, projectRoot); + } +} + +async function runWatchBuild( + projectRoot: string, + options: ScssBuildExecutorSchema, + deps: BuildDependencies, +): Promise<{ success: boolean }> { + const bundlesDir = options.bundlesDir || DEFAULT_BUNDLES_DIR; + const cssOutputDir = path.resolve(projectRoot, options.cssOutputDir || DEFAULT_CSS_OUTPUT_DIR); + const watchDir = path.resolve(projectRoot, 'scss'); + const watchBundleNames = getWatchBundleNames(options); + + const rebuild = async (): Promise => { await generateScssBundles(projectRoot, bundlesDir, deps); await ensureDir(cssOutputDir); - const sources = await resolveSourceFiles(projectRoot, options); - const existingSources = sources.filter((source) => fs.existsSync(source)); + const sources = resolveSourcesByBundleNames(projectRoot, bundlesDir, watchBundleNames); + for (const source of sources) { + await compileFile(source, cssOutputDir, 'all', deps, projectRoot); + } + + await copyAssets(projectRoot, cssOutputDir); + }; + + await rebuild(); + logger.info('scss-build watch mode is watching for changes...'); + + return await new Promise<{ success: boolean }>((resolve) => { + let timer: NodeJS.Timeout | undefined; + let busy = false; + + const scheduleRebuild = () => { + if (timer) { + clearTimeout(timer); + } + + timer = setTimeout(async () => { + if (busy) { + return; + } + + busy = true; + try { + await rebuild(); + logger.info('scss-build watch: rebuild complete'); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + logger.error(`scss-build watch rebuild failed: ${message}`); + } finally { + busy = false; + } + }, 200); + }; + + const watcher = fs.watch( + watchDir, + { recursive: true }, + (_eventType, fileName) => { + if (!fileName || !fileName.endsWith('.scss')) { + return; + } + scheduleRebuild(); + }, + ); + + const stopWatcher = () => { + watcher.close(); + if (timer) { + clearTimeout(timer); + } + resolve({ success: true }); + }; - for (const source of existingSources) { - logger.verbose(`Compiling ${source}`); - await compileFile(source, cssOutputDir, options, deps, projectRoot); + process.once('SIGINT', stopWatcher); + process.once('SIGTERM', stopWatcher); + }); +} + +const runExecutor: PromiseExecutor = async (options, context) => { + const projectRoot = resolveProjectPath(context); + + try { + const deps = loadDependencies(projectRoot); + if (options.watch) { + return await runWatchBuild(projectRoot, options, deps); } + await runSingleBuild(projectRoot, options, deps); return { success: true }; } catch (error) { const message = error instanceof Error ? error.message : String(error); diff --git a/packages/nx-infra-plugin/src/executors/scss-build/schema.json b/packages/nx-infra-plugin/src/executors/scss-build/schema.json index 168f3b7da931..46150b76b51a 100644 --- a/packages/nx-infra-plugin/src/executors/scss-build/schema.json +++ b/packages/nx-infra-plugin/src/executors/scss-build/schema.json @@ -25,6 +25,23 @@ "items": { "type": "string" } + }, + "watch": { + "type": "boolean", + "description": "Watch SCSS sources and rebuild on changes", + "default": false + }, + "bundles": { + "description": "Bundle names for watch mode (array or comma-separated string)", + "oneOf": [ + { + "type": "array", + "items": { "type": "string" } + }, + { + "type": "string" + } + ] } }, "required": ["mode"] diff --git a/packages/nx-infra-plugin/src/executors/scss-build/schema.ts b/packages/nx-infra-plugin/src/executors/scss-build/schema.ts index 2df3f2853b66..cbbcb5dccd3d 100644 --- a/packages/nx-infra-plugin/src/executors/scss-build/schema.ts +++ b/packages/nx-infra-plugin/src/executors/scss-build/schema.ts @@ -3,4 +3,6 @@ export interface ScssBuildExecutorSchema { bundlesDir?: string; cssOutputDir?: string; devBundles?: string[]; + watch?: boolean; + bundles?: string[] | string; } From 4b31bab9722918ef89850996eed26e55d60374b3 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Thu, 30 Apr 2026 00:06:25 +0200 Subject: [PATCH 05/23] remove gulp from devextreme-scss --- .github/workflows/themebuilder_tests.yml | 2 +- .../devextreme-scss/build/gulp-data-uri.js | 46 ----- .../devextreme-scss/build/style-compiler.js | 164 ------------------ packages/devextreme-scss/gulpfile.js | 40 ----- packages/devextreme-scss/package.json | 10 -- packages/devextreme-scss/project.json | 6 +- packages/devextreme/package.json | 2 +- pnpm-lock.yaml | 30 ---- 8 files changed, 4 insertions(+), 296 deletions(-) delete mode 100644 packages/devextreme-scss/build/gulp-data-uri.js delete mode 100644 packages/devextreme-scss/build/style-compiler.js delete mode 100644 packages/devextreme-scss/gulpfile.js diff --git a/.github/workflows/themebuilder_tests.yml b/.github/workflows/themebuilder_tests.yml index bd22d691b811..40554c1d8780 100644 --- a/.github/workflows/themebuilder_tests.yml +++ b/.github/workflows/themebuilder_tests.yml @@ -52,7 +52,7 @@ jobs: - name: Build etalon bundles working-directory: ./packages/devextreme-scss - run: pnpm exec gulp style-compiler-themes-ci + run: pnpm --workspace-root nx run devextreme-scss:build:ci - name: Build working-directory: ./packages/devextreme-themebuilder diff --git a/packages/devextreme-scss/build/gulp-data-uri.js b/packages/devextreme-scss/build/gulp-data-uri.js deleted file mode 100644 index 699d1a4d51c2..000000000000 --- a/packages/devextreme-scss/build/gulp-data-uri.js +++ /dev/null @@ -1,46 +0,0 @@ -import path, { dirname } from 'path'; -import fs from 'fs'; -import sass from 'sass-embedded'; -import { fileURLToPath } from 'url'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -const dataUriRegex = /data-uri\((?:'(image\/svg\+xml;charset=UTF-8)',\s)?['"]?([^)'"]+)['"]?\)/g; - -const svg = (buffer, svgEncoding) => { - const encoding = svgEncoding || 'image/svg+xml;charset=UTF-8'; - const svg = encodeURIComponent(buffer.toString()); - - return `"data:${encoding},${svg}"`; -}; - -const img = (buffer, ext) => { - return `"data:image/${ext};base64,${buffer.toString('base64')}"`; -}; - -const handler = (_, svgEncoding, fileName) => { - const relativePath = path.join(__dirname, '..', fileName); - const filePath = path.resolve(relativePath); - const ext = filePath.split('.').pop(); - const data = fs.readFileSync(filePath); - const buffer = Buffer.from(data); - const escapedString = ext === 'svg' ? svg(buffer, svgEncoding) : img(buffer, ext); - return `url(${escapedString})`; -}; - -const sassFunction = (args) => { - const getTextFromSass = (sassValue) => sassValue.assertString().text; - const argList = args[0].asList; - const hasEncoding = argList.size === 2; - const encoding = hasEncoding ? getTextFromSass(argList.get(0)) : null; - const url = getTextFromSass(argList.get(hasEncoding ? 1 : 0)); - - return new sass.SassString(handler(null, encoding, url), { quotes: false }); -}; - -export const resolveDataUri = (content) => content.replace(dataUriRegex, handler); - -export const sassFunctions = { - 'data-uri($args...)': sassFunction, -}; diff --git a/packages/devextreme-scss/build/style-compiler.js b/packages/devextreme-scss/build/style-compiler.js deleted file mode 100644 index 29f70deab20f..000000000000 --- a/packages/devextreme-scss/build/style-compiler.js +++ /dev/null @@ -1,164 +0,0 @@ -import gulp from 'gulp'; -const { task, src, parallel, series, dest, watch } = gulp; - -import { join } from 'path'; -import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs'; -import replace from 'gulp-replace'; -import plumber from 'gulp-plumber'; -import gulpSass from 'gulp-sass'; -import sassEmbedded from 'sass-embedded'; -import CleanCss from 'clean-css'; -import through from 'through2'; -import parseArguments from 'minimist'; -import autoprefixer from 'gulp-autoprefixer'; -import { createRequire } from 'module'; -import { fileURLToPath } from 'url'; -import { dirname } from 'path'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -const require = createRequire(import.meta.url); -const cleanCssSanitizeOptions = require('./clean-css-options.json'); -const cleanCssOptions = require('../../devextreme-themebuilder/src/data/clean-css-options.json'); -const { starLicense } = require('../../devextreme/build/gulp/header-pipes.js'); - -const { getThemes } = require('./theme-options.cjs'); -import { sassFunctions } from './gulp-data-uri.js'; - -const sass = gulpSass(sassEmbedded); - -const cssArtifactsPath = join(process.cwd(), '..', 'devextreme', 'artifacts', 'css'); - -const DEFAULT_DEV_BUNDLE_NAMES = [ - 'light', - 'light.compact', - 'dark', - 'contrast', - 'material.blue.light', - 'material.blue.light.compact', - 'material.blue.dark', - 'fluent.blue.light', - 'fluent.blue.light.compact', - 'fluent.blue.dark', - 'fluent.saas.light', - 'fluent.saas.dark', -]; - -const getBundleSourcePath = name => `scss/bundles/dx.${name}.scss`; - -const compileBundles = (bundles, isDevBundle) => { - return src(bundles) - .pipe(plumber(e => { - console.log(e); - this.emit('end'); - })) - .on('data', (chunk) => console.log('Build: ', chunk.path)) - .pipe(sass({ - functions: sassFunctions - })) - .pipe(autoprefixer()) - .pipe(through.obj((file, enc, callback) => { - const content = file.contents.toString(); - new CleanCss(isDevBundle ? cleanCssOptions : cleanCssSanitizeOptions).minify(content, (_, css) => { - file.contents = new Buffer.from(css.styles); - callback(null, file); - }); - })) - .pipe(starLicense()) - .pipe(replace(/([\s\S]*)(@charset.*?;\s)/, '$2$1')) - .pipe(dest(cssArtifactsPath)); -}; - -function saveBundleFile(folder, fileName, content) { - const bundlePath = join(folder, fileName); - if(!existsSync(folder)) mkdirSync(folder); - writeFileSync(bundlePath, content); -} - -function generateScssBundleName(theme, size, color, mode) { - return 'dx' + - (theme === 'material' || theme === 'fluent' - ? `.${theme}` - : '') - + `.${color}` + - (mode ? `.${mode}` : '') + - (size === 'default' ? '' : '.compact') + - '.scss'; -} - -function generateScssBundles(bundlesFolder, getBundleContent) { - const saveBundle = (theme, size, color, mode) => { - const bundleName = generateScssBundleName(theme, size, color, mode); - const content = getBundleContent(theme, size, color, mode); - - saveBundleFile(bundlesFolder, bundleName, content); - }; - - getThemes().forEach(([theme, size, color, mode]) => saveBundle(theme, size, color, mode)); -} - -function createBundles(callback) { - const bundlesFolder = join(process.cwd(), 'scss', 'bundles'); - const readTemplate = (theme) => readFileSync(join(__dirname, `bundle-template.${theme}.scss`), 'utf8'); - const getBundleContent = (theme, size, color, mode) => { - const bundleTemplate = readTemplate(theme); - const bundleContent = bundleTemplate - .replace('$COLOR', color) - .replace('$SIZE', size) - .replace('$MODE', mode); - return bundleContent; - }; - - generateScssBundles(bundlesFolder, getBundleContent); - saveBundleFile(bundlesFolder, 'dx.common.scss', readTemplate('common')); - - if(callback) callback(); -} - -task('create-scss-bundles', createBundles); - -task('copy-fonts-and-icons', () => { - return src(['fonts/**/*', 'icons/**/*'], { base: '.' }) - .pipe(dest(cssArtifactsPath)); -}); - -task('compile-themes-all', () => compileBundles(getBundleSourcePath('*'))); -task('compile-themes-dev', () => compileBundles(DEFAULT_DEV_BUNDLE_NAMES.map(getBundleSourcePath), true)); - -task('style-compiler-themes', series( - 'create-scss-bundles', - parallel( - 'compile-themes-all', - 'copy-fonts-and-icons' - ) -)); - -task('style-compiler-themes-ci', series( - 'create-scss-bundles', - parallel( - 'compile-themes-dev', - 'copy-fonts-and-icons' - ) -)); - -task('style-compiler-themes-watch', () => { - const args = parseArguments(process.argv); - const bundlesArg = args['bundles']; - - const bundles = ( - bundlesArg - ? bundlesArg.split(',') - : DEFAULT_DEV_BUNDLE_NAMES) - .map((bundle) => { - const sourcePath = getBundleSourcePath(bundle); - if(existsSync(sourcePath)) { - return sourcePath; - } - console.log(`${sourcePath} file does not exists`); - return null; - }); - - watch('scss/**/*', parallel(() => compileBundles(bundles), 'copy-fonts-and-icons')) - .on('ready', () => console.log('style-compiler-themes task is watching for changes...')); -}); diff --git a/packages/devextreme-scss/gulpfile.js b/packages/devextreme-scss/gulpfile.js deleted file mode 100644 index 0494cad08db7..000000000000 --- a/packages/devextreme-scss/gulpfile.js +++ /dev/null @@ -1,40 +0,0 @@ -/* eslint-env node */ -/* eslint-disable no-console */ - -import gulp from 'gulp'; -import cache from 'gulp-cache'; -import { createRequire } from 'module'; - -const require = createRequire(import.meta.url); -const env = require('../devextreme/build/gulp/env-variables.js'); -const del = require('del'); - -gulp.task('clean', function(callback) { - del.sync([ - '../devextreme/artifacts/css/**', - '../devextreme/scss/bundles/**' - ], { force: true }); - cache.clearAll(); - callback(); -}); - -import './build/style-compiler.js'; - -if(env.TEST_CI) { - console.warn('Using test CI mode!'); -} - -function createStyleCompilerBatch() { - return gulp.series( - 'clean', - env.TEST_CI - ? ['style-compiler-themes-ci'] - : ['style-compiler-themes'] - ); -} - -gulp.task('default', createStyleCompilerBatch()); - -gulp.task('watch', gulp.series( - 'style-compiler-themes-watch' -)); diff --git a/packages/devextreme-scss/package.json b/packages/devextreme-scss/package.json index 4b6c18f6e3f3..2f7a4a3c34a8 100644 --- a/packages/devextreme-scss/package.json +++ b/packages/devextreme-scss/package.json @@ -3,20 +3,10 @@ "type": "module", "devDependencies": { "clean-css": "5.3.3", - "del": "2.2.2", - "gulp": "4.0.2", - "gulp-autoprefixer": "10.0.0", - "gulp-cache": "1.1.3", - "gulp-plumber": "1.2.1", - "gulp-replace": "0.6.1", - "gulp-sass": "6.0.1", - "gulp-shell": "0.8.0", - "minimist": "1.2.8", "sass-embedded": "1.93.3", "stylelint": "15.11.0", "stylelint-config-standard-scss": "9.0.0", "stylelint-scss": "6.10.0", - "through2": "2.0.5", "ts-jest": "29.1.2" }, "scripts": { diff --git a/packages/devextreme-scss/project.json b/packages/devextreme-scss/project.json index 2fc6380391ea..f720e5dfbfdd 100644 --- a/packages/devextreme-scss/project.json +++ b/packages/devextreme-scss/project.json @@ -92,8 +92,7 @@ "{projectRoot}/fonts/**/*", "{projectRoot}/icons/**/*", "{projectRoot}/images/**/*", - "{projectRoot}/scss/**/*", - "{projectRoot}/gulpfile.js" + "{projectRoot}/scss/**/*" ], "outputs": [ "{projectRoot}/scss/bundles", @@ -118,8 +117,7 @@ "{projectRoot}/fonts/**/*", "{projectRoot}/icons/**/*", "{projectRoot}/images/**/*", - "{projectRoot}/scss/**/*", - "{projectRoot}/gulpfile.js" + "{projectRoot}/scss/**/*" ], "outputs": [ "{projectRoot}/scss/bundles", diff --git a/packages/devextreme/package.json b/packages/devextreme/package.json index e5fcedb75b3c..6238f00ad6dc 100644 --- a/packages/devextreme/package.json +++ b/packages/devextreme/package.json @@ -231,7 +231,7 @@ "build:testcafe": "cross-env DEVEXTREME_TEST_CI=TRUE BUILD_ESM_PACKAGE=true BUILD_TESTCAFE=TRUE gulp default", "build-npm-devextreme": "cross-env BUILD_ESM_PACKAGE=true gulp default", "build-dist": "cross-env BUILD_ESM_PACKAGE=true gulp default --uglify", - "build-themes": "gulp style-compiler-themes", + "build-themes": "pnpm --workspace-root nx run devextreme-scss:build:themes && pnpm --workspace-root nx run devextreme-scss:copy:assets", "build:react": "gulp generate-react", "build:react:watch": "gulp generate-react-watch", "build:react:typescript": "gulp generate-react-typescript", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c578ed08dca9..23741402a798 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2122,33 +2122,6 @@ importers: clean-css: specifier: 5.3.3 version: 5.3.3 - del: - specifier: 2.2.2 - version: 2.2.2 - gulp: - specifier: 4.0.2 - version: 4.0.2 - gulp-autoprefixer: - specifier: 10.0.0 - version: 10.0.0(gulp@4.0.2) - gulp-cache: - specifier: 1.1.3 - version: 1.1.3 - gulp-plumber: - specifier: 1.2.1 - version: 1.2.1 - gulp-replace: - specifier: 0.6.1 - version: 0.6.1 - gulp-sass: - specifier: 6.0.1 - version: 6.0.1 - gulp-shell: - specifier: 0.8.0 - version: 0.8.0 - minimist: - specifier: 1.2.8 - version: 1.2.8 sass-embedded: specifier: 1.93.3 version: 1.93.3 @@ -2161,9 +2134,6 @@ importers: stylelint-scss: specifier: 6.10.0 version: 6.10.0(stylelint@15.11.0(typescript@5.9.3)) - through2: - specifier: 2.0.5 - version: 2.0.5 ts-jest: specifier: 29.1.2 version: 29.1.2(@babel/core@7.29.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.0))(jest@30.2.0(@types/node@25.7.0)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.7.0)(typescript@5.9.3)))(typescript@5.9.3) From 87774e33cdb0f295d6a0d46d1089a556057ff38c Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Thu, 30 Apr 2026 00:26:08 +0200 Subject: [PATCH 06/23] add tests --- .../executors/scss-build/executor.e2e.spec.ts | 181 +++++++++++++++++- .../src/executors/scss-build/executor.ts | 5 +- 2 files changed, 182 insertions(+), 4 deletions(-) diff --git a/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts b/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts index 9a21bbb3834e..5c1eed640695 100644 --- a/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts +++ b/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts @@ -1,5 +1,182 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import executor from './executor'; +import { ScssBuildExecutorSchema } from './schema'; +import { createMockContext, createTempDir, cleanupTempDir } from '../../utils/test-utils'; +import { writeFileText, writeJson, readFileText } from '../../utils'; + +function createMockModules(workspaceRoot: string, projectRoot: string): void { + const projectNodeModules = path.join(projectRoot, 'node_modules', 'sass-embedded'); + fs.mkdirSync(projectNodeModules, { recursive: true }); + fs.writeFileSync( + path.join(projectNodeModules, 'index.js'), + [ + 'class SassString {', + ' constructor(value) { this.value = value; }', + '}', + 'module.exports = {', + ' SassString,', + " compile: () => ({ css: '@charset \"UTF-8\"; .a{display:flex}' })", + '};', + '', + ].join('\n'), + 'utf8', + ); + + const workspaceNodeModules = path.join(workspaceRoot, 'node_modules'); + fs.mkdirSync(workspaceNodeModules, { recursive: true }); + + const postcssDir = path.join(workspaceNodeModules, 'postcss'); + fs.mkdirSync(postcssDir, { recursive: true }); + fs.writeFileSync( + path.join(postcssDir, 'index.js'), + [ + 'module.exports = function postcss() {', + ' return {', + ' process: async (css) => ({ css: css + "/*prefixed*/" })', + ' };', + '};', + '', + ].join('\n'), + 'utf8', + ); + + const autoprefixerDir = path.join(workspaceNodeModules, 'autoprefixer'); + fs.mkdirSync(autoprefixerDir, { recursive: true }); + fs.writeFileSync( + path.join(autoprefixerDir, 'index.js'), + 'module.exports = function autoprefixer() { return { postcssPlugin: "autoprefixer" }; };', + 'utf8', + ); + + const cleanCssDir = path.join(workspaceNodeModules, 'clean-css'); + fs.mkdirSync(cleanCssDir, { recursive: true }); + fs.writeFileSync( + path.join(cleanCssDir, 'index.js'), + [ + 'module.exports = class CleanCss {', + ' constructor(options) { this.options = options || {}; }', + ' minify(css) {', + ' return { styles: css + "/*min:" + (this.options.profile || "none") + "*/" };', + ' }', + '};', + '', + ].join('\n'), + 'utf8', + ); +} + +async function setupProjectStructure(workspaceRoot: string): Promise { + const projectRoot = path.join(workspaceRoot, 'packages', 'devextreme-scss'); + const buildDir = path.join(projectRoot, 'build'); + fs.mkdirSync(buildDir, { recursive: true }); + + await writeJson(path.join(workspaceRoot, 'package.json'), { name: 'workspace' }); + await writeJson(path.join(projectRoot, 'package.json'), { name: 'devextreme-scss' }); + + await writeJson(path.join(projectRoot, 'build', 'clean-css-options.json'), { profile: 'all' }); + + const themebuilderDataDir = path.join( + workspaceRoot, + 'packages', + 'devextreme-themebuilder', + 'src', + 'data', + ); + fs.mkdirSync(themebuilderDataDir, { recursive: true }); + await writeJson(path.join(themebuilderDataDir, 'clean-css-options.json'), { profile: 'ci' }); + + const devextremeDir = path.join(workspaceRoot, 'packages', 'devextreme'); + fs.mkdirSync(devextremeDir, { recursive: true }); + await writeJson(path.join(devextremeDir, 'package.json'), { version: '26.1.0-test' }); + + await writeFileText( + path.join(buildDir, 'theme-options.cjs'), + [ + 'module.exports = {', + ' getThemes: () => [', + " ['generic', 'default', 'light'],", + ' ],', + '};', + '', + ].join('\n'), + ); + + await writeFileText(path.join(buildDir, 'bundle-template.common.scss'), '.common { color: red; }'); + await writeFileText(path.join(buildDir, 'bundle-template.generic.scss'), '.generic-$COLOR { color: red; }'); + + createMockModules(workspaceRoot, projectRoot); + return projectRoot; +} + describe('ScssBuildExecutor E2E', () => { - it('has test placeholder for native pipeline', () => { - expect(true).toBe(true); + let tempDir: string; + + beforeEach(() => { + tempDir = createTempDir('nx-scss-build-e2e-'); + }); + + afterEach(() => { + cleanupTempDir(tempDir); + }); + + it('builds all mode bundles and applies license/minification profile', async () => { + const projectRoot = await setupProjectStructure(tempDir); + const context = createMockContext({ + root: tempDir, + projectName: 'devextreme-scss', + projectRoot: 'packages/devextreme-scss', + }); + + const options: ScssBuildExecutorSchema = { mode: 'all', cssOutputDir: './artifacts/css' }; + const result = await executor(options, context); + + expect(result.success).toBe(true); + expect(fs.existsSync(path.join(projectRoot, 'scss', 'bundles', 'dx.light.scss'))).toBe(true); + expect(fs.existsSync(path.join(projectRoot, 'scss', 'bundles', 'dx.common.scss'))).toBe(true); + + const cssDir = path.join(projectRoot, 'artifacts', 'css'); + const generatedCssFiles = fs + .readdirSync(cssDir) + .filter((name) => name.endsWith('.css')) + .sort(); + expect(generatedCssFiles.length).toBeGreaterThan(0); + expect(generatedCssFiles).toContain('dx.common.css'); + + const commonCss = await readFileText(path.join(cssDir, 'dx.common.css')); + + expect(commonCss).toContain('Version: 26.1.0-test'); + expect(commonCss).toContain('/*min:all*/'); + expect(commonCss).toContain('DevExtreme (dx.common.css)'); + }); + + it('builds ci mode only for selected dev bundles and uses ci profile', async () => { + const projectRoot = await setupProjectStructure(tempDir); + const context = createMockContext({ + root: tempDir, + projectName: 'devextreme-scss', + projectRoot: 'packages/devextreme-scss', + }); + + const options: ScssBuildExecutorSchema = { + mode: 'ci', + devBundles: ['light'], + cssOutputDir: './artifacts/css', + }; + const result = await executor(options, context); + + expect(result.success).toBe(true); + + const cssDir = path.join(projectRoot, 'artifacts', 'css'); + const generatedCssFiles = fs + .readdirSync(cssDir) + .filter((name) => name.endsWith('.css')) + .sort(); + + expect(generatedCssFiles).toEqual(['dx.light.css']); + const lightCss = await readFileText(path.join(cssDir, 'dx.light.css')); + expect(lightCss).toContain('/*min:ci*/'); + + expect(fs.existsSync(path.join(projectRoot, 'scss', 'bundles', 'dx.common.scss'))).toBe(true); }); }); diff --git a/packages/nx-infra-plugin/src/executors/scss-build/executor.ts b/packages/nx-infra-plugin/src/executors/scss-build/executor.ts index e55b2db55256..f1be4ee994c7 100644 --- a/packages/nx-infra-plugin/src/executors/scss-build/executor.ts +++ b/packages/nx-infra-plugin/src/executors/scss-build/executor.ts @@ -4,7 +4,7 @@ import * as path from 'path'; import { createRequire } from 'module'; import { glob } from 'glob'; import { ScssBuildExecutorSchema } from './schema'; -import { resolveProjectPath } from '../../utils/path-resolver'; +import { normalizeGlobPathForWindows, resolveProjectPath } from '../../utils/path-resolver'; import { ensureDir, readFileText, writeFileText } from '../../utils/file-operations'; const DEFAULT_BUNDLES_DIR = './scss/bundles'; @@ -155,7 +155,8 @@ function resolveSourceFiles( return Promise.resolve(bundleNames.map((name) => path.join(bundlesDir, `dx.${name}.scss`))); } - return glob(path.join(bundlesDir, 'dx.*.scss'), { nodir: true }); + const pattern = normalizeGlobPathForWindows(path.join(bundlesDir, 'dx.*.scss')); + return glob(pattern, { nodir: true }); } function createDataUriFunction(projectRoot: string, sass: any): (args: any[]) => any { From 19a309dc8e4d8c93ea9d4903ceac1e90b3eb25d3 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Thu, 30 Apr 2026 13:35:23 +0200 Subject: [PATCH 07/23] fix executor for dex-scss --- .../src/modules/compile-manager.ts | 2 +- .../src/modules/post-compiler.ts | 30 ++++++-- .../executors/scss-build/executor.e2e.spec.ts | 12 +++- .../src/executors/scss-build/executor.ts | 68 +++++++++++-------- 4 files changed, 73 insertions(+), 39 deletions(-) diff --git a/packages/devextreme-themebuilder/src/modules/compile-manager.ts b/packages/devextreme-themebuilder/src/modules/compile-manager.ts index d7a112a26035..d98d4127d7c0 100644 --- a/packages/devextreme-themebuilder/src/modules/compile-manager.ts +++ b/packages/devextreme-themebuilder/src/modules/compile-manager.ts @@ -69,7 +69,7 @@ export default class CompileManager { css = removeExternalResources(css); } - css = addInfoHeader(css, version); + css = addInfoHeader(css, version, true); return { compiledMetadata: compileData.changedVariables, diff --git a/packages/devextreme-themebuilder/src/modules/post-compiler.ts b/packages/devextreme-themebuilder/src/modules/post-compiler.ts index 30ce6798a514..0ecc5a5f01fd 100644 --- a/packages/devextreme-themebuilder/src/modules/post-compiler.ts +++ b/packages/devextreme-themebuilder/src/modules/post-compiler.ts @@ -10,19 +10,39 @@ export function addBasePath(css: string | Buffer, basePath: string): string { return css.toString().replace(/(url\()("|')?(icons|fonts)/g, `$1$2${normalizedPath}$3`); } -export function addInfoHeader(css: string | Buffer, version: string): string { +function buildThemeBuilderInfoHeader(version: string): string { const generatedBy = '* Generated by the DevExpress ThemeBuilder'; const versionString = `* Version: ${version}`; const link = '* http://js.devexpress.com/ThemeBuilder/'; - const header = `/*${generatedBy}\n${versionString}\n${link}\n*/\n\n`; + return `/*${generatedBy}\n${versionString}\n${link}\n*/\n\n`; +} + +export function addInfoHeader( + css: string | Buffer, + version: string, + appendInfoHeaderAfterBody = false, +): string { + const header = buildThemeBuilderInfoHeader(version); const source = css.toString(); const encoding = '@charset "UTF-8";'; - if (source.startsWith(encoding)) { - return `${encoding}\n${header}${source.replace(`${encoding}\n`, '')}`; + // clean-css may emit @charset immediately followed by :root / @import with no newline. + const charsetPrefix = /^@charset\s+"utf-8";\s*/i; + const match = source.match(charsetPrefix); + if (match) { + const rest = source.slice(match[0].length).trimStart(); + + if (appendInfoHeaderAfterBody) { + const joined = `${encoding.trimEnd()}${rest}`.replace( + /^(@charset\s+"utf-8";)\s+/i, + '$1', + ); + return `${joined}\n${header}`; + } + return `${encoding}\n${header}${rest}`; } - return `${header}${css}`; + return `${header}${source}`; } export async function cleanCss(css: string): Promise { diff --git a/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts b/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts index 5c1eed640695..c7c8c46bd971 100644 --- a/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts +++ b/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts @@ -16,7 +16,7 @@ function createMockModules(workspaceRoot: string, projectRoot: string): void { '}', 'module.exports = {', ' SassString,', - " compile: () => ({ css: '@charset \"UTF-8\"; .a{display:flex}' })", + ' compile: () => ({ css: \'@charset "UTF-8"; .a{display:flex}\' })', '};', '', ].join('\n'), @@ -102,8 +102,14 @@ async function setupProjectStructure(workspaceRoot: string): Promise { ].join('\n'), ); - await writeFileText(path.join(buildDir, 'bundle-template.common.scss'), '.common { color: red; }'); - await writeFileText(path.join(buildDir, 'bundle-template.generic.scss'), '.generic-$COLOR { color: red; }'); + await writeFileText( + path.join(buildDir, 'bundle-template.common.scss'), + '.common { color: red; }', + ); + await writeFileText( + path.join(buildDir, 'bundle-template.generic.scss'), + '.generic-$COLOR { color: red; }', + ); createMockModules(workspaceRoot, projectRoot); return projectRoot; diff --git a/packages/nx-infra-plugin/src/executors/scss-build/executor.ts b/packages/nx-infra-plugin/src/executors/scss-build/executor.ts index f1be4ee994c7..75fdeeee4d9d 100644 --- a/packages/nx-infra-plugin/src/executors/scss-build/executor.ts +++ b/packages/nx-infra-plugin/src/executors/scss-build/executor.ts @@ -29,7 +29,7 @@ const EULA_URL = 'https://js.devexpress.com/Licensing/'; interface BuildDependencies { sass: any; postcss: any; - autoprefixer: () => any; + autoprefixer: (options?: { overrideBrowserslist?: string[] }) => any; CleanCss: new (options: unknown) => { minify: (input: string) => { styles: string } }; themeOptions: { getThemes: () => Array<[string, string, string, string?]> }; cleanCssSanitizeOptions: unknown; @@ -51,9 +51,13 @@ function resolveDataUri(filePath: string, svgEncoding?: string): string { return `data:image/${ext};base64,${data.toString('base64')}`; } -function createLicenseHeader(fileName: string, version: string): string { +/** + * Same shape as `packages/devextreme/build/gulp/license-header.txt` with + * `gulp-header` `commentType: '*'` (starLicense) — matches legacy Gulp output. + */ +function createStarLicenseHeader(fileName: string, version: string): string { return [ - '/*!', + '/**', `* DevExtreme (${fileName.replace(/\\/g, '/')})`, `* Version: ${version}`, `* Build date: ${new Date().toDateString()}`, @@ -65,24 +69,24 @@ function createLicenseHeader(fileName: string, version: string): string { ].join('\n'); } -function moveCharsetToTop(css: string): string { - const match = css.match(/@charset\s+[^;]+;\s*/); - if (!match) { - return css; - } - - const charset = match[0]; - const withoutCharset = css.replace(charset, ''); - return charset + withoutCharset; +/** + * Mirrors `style-compiler.js`: starLicense prepend, then + * `.replace(/([\s\S]*)(@charset.*?;\s)/, '$2$1')` so `@charset` is the first bytes of output. + */ +function prependLicenseAndMoveCharsetFirst(minifiedCss: string, license: string): string { + const withLicense = `${license}${minifiedCss}`; + return withLicense.replace(/([\s\S]*)(@charset[^;]+;\s*)/, '$2$1'); } function generateBundleName(theme: string, size: string, color: string, mode?: string): string { - return 'dx' + return ( + 'dx' + (theme === 'material' || theme === 'fluent' ? `.${theme}` : '') + `.${color}` + (mode ? `.${mode}` : '') + (size === 'default' ? '' : '.compact') - + '.scss'; + + '.scss' + ); } async function generateScssBundles( @@ -100,7 +104,10 @@ async function generateScssBundles( const themes = deps.themeOptions.getThemes(); for (const [theme, size, color, mode] of themes) { const template = await readTemplate(theme); - const content = template.replace('$COLOR', color).replace('$SIZE', size).replace('$MODE', mode || ''); + const content = template + .replace('$COLOR', color) + .replace('$SIZE', size) + .replace('$MODE', mode || ''); const fileName = generateBundleName(theme, size, color, mode); await writeFileText(path.join(resolvedBundlesDir, fileName), content); } @@ -121,11 +128,14 @@ function loadDependencies(projectRoot: string): BuildDependencies { themeOptions: projectRequire(path.resolve(projectRoot, 'build/theme-options.cjs')) as { getThemes: () => Array<[string, string, string, string?]>; }, - cleanCssSanitizeOptions: projectRequire(path.resolve(projectRoot, 'build/clean-css-options.json')), + cleanCssSanitizeOptions: projectRequire( + path.resolve(projectRoot, 'build/clean-css-options.json'), + ), cleanCssDevOptions: workspaceRequire( path.resolve(projectRoot, '../devextreme-themebuilder/src/data/clean-css-options.json'), ), - devextremeVersion: workspaceRequire(path.resolve(projectRoot, '../devextreme/package.json')).version, + devextremeVersion: workspaceRequire(path.resolve(projectRoot, '../devextreme/package.json')) + .version, }; } @@ -188,15 +198,17 @@ async function compileFile( const postcssFactory = (deps.postcss as unknown as { default?: any }).default || deps.postcss; const prefixed = await postcssFactory([deps.autoprefixer()]).process(compiled.css, { - from: undefined, + from: sourceFile, }); - const minifierOptions = minifyProfile === 'ci' ? deps.cleanCssDevOptions : deps.cleanCssSanitizeOptions; + const minifierOptions = + minifyProfile === 'ci' ? deps.cleanCssDevOptions : deps.cleanCssSanitizeOptions; const minifier = new deps.CleanCss(minifierOptions); const minified = minifier.minify(prefixed.css).styles; const outFileName = path.basename(sourceFile, '.scss') + '.css'; - const withHeader = createLicenseHeader(outFileName, deps.devextremeVersion) + moveCharsetToTop(minified); + const license = createStarLicenseHeader(outFileName, deps.devextremeVersion); + const withHeader = prependLicenseAndMoveCharsetFirst(minified, license); await writeFileText(path.join(outputDir, outFileName), withHeader); } @@ -319,16 +331,12 @@ async function runWatchBuild( }, 200); }; - const watcher = fs.watch( - watchDir, - { recursive: true }, - (_eventType, fileName) => { - if (!fileName || !fileName.endsWith('.scss')) { - return; - } - scheduleRebuild(); - }, - ); + const watcher = fs.watch(watchDir, { recursive: true }, (_eventType, fileName) => { + if (!fileName || !fileName.endsWith('.scss')) { + return; + } + scheduleRebuild(); + }); const stopWatcher = () => { watcher.close(); From bd609270f9e262e00125dfe39acb04a32716bf96 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Tue, 5 May 2026 23:51:45 +0200 Subject: [PATCH 08/23] fix review comments --- .github/workflows/themebuilder_tests.yml | 2 +- packages/devextreme-scss/package.json | 2 +- .../tests/modules/post-compiler.test.ts | 17 ++++++++++++++ packages/devextreme/package.json | 2 +- .../executors/scss-build/executor.e2e.spec.ts | 18 +++++++++++++++ .../src/executors/scss-build/executor.ts | 23 +++++++++++++------ 6 files changed, 54 insertions(+), 10 deletions(-) diff --git a/.github/workflows/themebuilder_tests.yml b/.github/workflows/themebuilder_tests.yml index 40554c1d8780..45d9c5ce31fc 100644 --- a/.github/workflows/themebuilder_tests.yml +++ b/.github/workflows/themebuilder_tests.yml @@ -52,7 +52,7 @@ jobs: - name: Build etalon bundles working-directory: ./packages/devextreme-scss - run: pnpm --workspace-root nx run devextreme-scss:build:ci + run: pnpm --workspace-root nx build:ci devextreme-scss - name: Build working-directory: ./packages/devextreme-themebuilder diff --git a/packages/devextreme-scss/package.json b/packages/devextreme-scss/package.json index 2f7a4a3c34a8..234910000bf6 100644 --- a/packages/devextreme-scss/package.json +++ b/packages/devextreme-scss/package.json @@ -13,7 +13,7 @@ "build": "pnpm --workspace-root nx build devextreme-scss", "lint": "stylelint scss/widgets", "test": "jest --no-coverage --runInBand --config=./tests/jest.config.json", - "watch": "pnpm --workspace-root nx run devextreme-scss:watch" + "watch": "pnpm --workspace-root nx run devextreme-scss --target=watch" }, "version": "26.1.2" } diff --git a/packages/devextreme-themebuilder/tests/modules/post-compiler.test.ts b/packages/devextreme-themebuilder/tests/modules/post-compiler.test.ts index 576834f8c4d0..41717ff21294 100644 --- a/packages/devextreme-themebuilder/tests/modules/post-compiler.test.ts +++ b/packages/devextreme-themebuilder/tests/modules/post-compiler.test.ts @@ -38,6 +38,23 @@ describe('PostCompiler', () => { + 'css'); }); + const themeBuilderInfoHeader = '/** Generated by the DevExpress ThemeBuilder\n' + + '* Version: 1.1.1\n' + + '* http://js.devexpress.com/ThemeBuilder/\n' + + '*/\n\n'; + + test('addInfoHeader - append after body, @charset glued to :root (CompileManager parity)', () => { + expect(addInfoHeader('@charset "utf-8";:root{}', '1.1.1', true)) + .toBe('@charset "UTF-8";:root{}\n' + + themeBuilderInfoHeader); + }); + + test('addInfoHeader - append after body, strips newline between @charset and @import', () => { + expect(addInfoHeader('@charset "UTF-8";\n@import url(https://example.com/a.css);', '1.1.1', true)) + .toBe('@charset "UTF-8";@import url(https://example.com/a.css);\n' + + themeBuilderInfoHeader); + }); + test('cleanCss', async () => { expect(await cleanCss('.c1 { color: #F00; } .c2 { color: #F00; }')) .toBe('.c1,\n.c2 {\n color: red;\n}'); diff --git a/packages/devextreme/package.json b/packages/devextreme/package.json index 6238f00ad6dc..4fe4400be758 100644 --- a/packages/devextreme/package.json +++ b/packages/devextreme/package.json @@ -231,7 +231,7 @@ "build:testcafe": "cross-env DEVEXTREME_TEST_CI=TRUE BUILD_ESM_PACKAGE=true BUILD_TESTCAFE=TRUE gulp default", "build-npm-devextreme": "cross-env BUILD_ESM_PACKAGE=true gulp default", "build-dist": "cross-env BUILD_ESM_PACKAGE=true gulp default --uglify", - "build-themes": "pnpm --workspace-root nx run devextreme-scss:build:themes && pnpm --workspace-root nx run devextreme-scss:copy:assets", + "build-themes": "pnpm --workspace-root nx build:themes devextreme-scss && pnpm --workspace-root nx copy:assets devextreme-scss", "build:react": "gulp generate-react", "build:react:watch": "gulp generate-react-watch", "build:react:typescript": "gulp generate-react-typescript", diff --git a/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts b/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts index c7c8c46bd971..3cb469341d10 100644 --- a/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts +++ b/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts @@ -64,6 +64,24 @@ function createMockModules(workspaceRoot: string, projectRoot: string): void { ].join('\n'), 'utf8', ); + + const chokidarDir = path.join(workspaceNodeModules, 'chokidar'); + fs.mkdirSync(chokidarDir, { recursive: true }); + fs.writeFileSync( + path.join(chokidarDir, 'index.js'), + [ + 'module.exports = {', + ' watch: function watch() {', + ' return {', + ' on: function on() { return this; },', + ' close: function close() { return Promise.resolve(); },', + ' };', + ' },', + '};', + '', + ].join('\n'), + 'utf8', + ); } async function setupProjectStructure(workspaceRoot: string): Promise { diff --git a/packages/nx-infra-plugin/src/executors/scss-build/executor.ts b/packages/nx-infra-plugin/src/executors/scss-build/executor.ts index 75fdeeee4d9d..583a3ba7cc96 100644 --- a/packages/nx-infra-plugin/src/executors/scss-build/executor.ts +++ b/packages/nx-infra-plugin/src/executors/scss-build/executor.ts @@ -30,6 +30,15 @@ interface BuildDependencies { sass: any; postcss: any; autoprefixer: (options?: { overrideBrowserslist?: string[] }) => any; + chokidar: { + watch: ( + paths: string | string[], + options?: Record, + ) => { + on: (event: string, handler: (...args: any[]) => void) => unknown; + close: () => Promise | void; + }; + }; CleanCss: new (options: unknown) => { minify: (input: string) => { styles: string } }; themeOptions: { getThemes: () => Array<[string, string, string, string?]> }; cleanCssSanitizeOptions: unknown; @@ -124,6 +133,7 @@ function loadDependencies(projectRoot: string): BuildDependencies { sass: projectRequire('sass-embedded'), postcss: workspaceRequire('postcss'), autoprefixer: workspaceRequire('autoprefixer'), + chokidar: workspaceRequire('chokidar'), CleanCss: workspaceRequire('clean-css'), themeOptions: projectRequire(path.resolve(projectRoot, 'build/theme-options.cjs')) as { getThemes: () => Array<[string, string, string, string?]>; @@ -288,6 +298,7 @@ async function runWatchBuild( const cssOutputDir = path.resolve(projectRoot, options.cssOutputDir || DEFAULT_CSS_OUTPUT_DIR); const watchDir = path.resolve(projectRoot, 'scss'); const watchBundleNames = getWatchBundleNames(options); + const minifyProfile: MinifyProfile = options.mode === 'ci' ? 'ci' : 'all'; const rebuild = async (): Promise => { await generateScssBundles(projectRoot, bundlesDir, deps); @@ -295,7 +306,7 @@ async function runWatchBuild( const sources = resolveSourcesByBundleNames(projectRoot, bundlesDir, watchBundleNames); for (const source of sources) { - await compileFile(source, cssOutputDir, 'all', deps, projectRoot); + await compileFile(source, cssOutputDir, minifyProfile, deps, projectRoot); } await copyAssets(projectRoot, cssOutputDir); @@ -331,15 +342,13 @@ async function runWatchBuild( }, 200); }; - const watcher = fs.watch(watchDir, { recursive: true }, (_eventType, fileName) => { - if (!fileName || !fileName.endsWith('.scss')) { - return; - } - scheduleRebuild(); + const watcher = deps.chokidar.watch(path.join(watchDir, '**/*.scss'), { + ignoreInitial: true, }); + watcher.on('all', scheduleRebuild); const stopWatcher = () => { - watcher.close(); + void watcher.close(); if (timer) { clearTimeout(timer); } From c6aee4694219e6efc8b79cfb0e3a5ac8c3b1b57b Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Wed, 6 May 2026 12:07:15 +0200 Subject: [PATCH 09/23] clean code --- packages/devextreme-themebuilder/src/modules/post-compiler.ts | 3 +-- packages/nx-infra-plugin/src/executors/scss-build/executor.ts | 4 ---- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/devextreme-themebuilder/src/modules/post-compiler.ts b/packages/devextreme-themebuilder/src/modules/post-compiler.ts index 0ecc5a5f01fd..b0357609a572 100644 --- a/packages/devextreme-themebuilder/src/modules/post-compiler.ts +++ b/packages/devextreme-themebuilder/src/modules/post-compiler.ts @@ -26,10 +26,9 @@ export function addInfoHeader( const header = buildThemeBuilderInfoHeader(version); const source = css.toString(); const encoding = '@charset "UTF-8";'; - - // clean-css may emit @charset immediately followed by :root / @import with no newline. const charsetPrefix = /^@charset\s+"utf-8";\s*/i; const match = source.match(charsetPrefix); + if (match) { const rest = source.slice(match[0].length).trimStart(); diff --git a/packages/nx-infra-plugin/src/executors/scss-build/executor.ts b/packages/nx-infra-plugin/src/executors/scss-build/executor.ts index 583a3ba7cc96..041e1096bf81 100644 --- a/packages/nx-infra-plugin/src/executors/scss-build/executor.ts +++ b/packages/nx-infra-plugin/src/executors/scss-build/executor.ts @@ -78,10 +78,6 @@ function createStarLicenseHeader(fileName: string, version: string): string { ].join('\n'); } -/** - * Mirrors `style-compiler.js`: starLicense prepend, then - * `.replace(/([\s\S]*)(@charset.*?;\s)/, '$2$1')` so `@charset` is the first bytes of output. - */ function prependLicenseAndMoveCharsetFirst(minifiedCss: string, license: string): string { const withLicense = `${license}${minifiedCss}`; return withLicense.replace(/([\s\S]*)(@charset[^;]+;\s*)/, '$2$1'); From 8e289f4ce2611a1ef520dcbecc98eee705adb52b Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Wed, 13 May 2026 23:47:36 +0200 Subject: [PATCH 10/23] refactor and optimization --- .github/workflows/themebuilder_tests.yml | 2 +- packages/devextreme-scss/package.json | 4 ++-- packages/devextreme-scss/project.json | 18 ++++++++++-------- packages/devextreme/package.json | 2 +- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/.github/workflows/themebuilder_tests.yml b/.github/workflows/themebuilder_tests.yml index 45d9c5ce31fc..30fcfca25d03 100644 --- a/.github/workflows/themebuilder_tests.yml +++ b/.github/workflows/themebuilder_tests.yml @@ -52,7 +52,7 @@ jobs: - name: Build etalon bundles working-directory: ./packages/devextreme-scss - run: pnpm --workspace-root nx build:ci devextreme-scss + run: pnpm nx build:ci devextreme-scss - name: Build working-directory: ./packages/devextreme-themebuilder diff --git a/packages/devextreme-scss/package.json b/packages/devextreme-scss/package.json index 234910000bf6..b22fe1e401ef 100644 --- a/packages/devextreme-scss/package.json +++ b/packages/devextreme-scss/package.json @@ -10,10 +10,10 @@ "ts-jest": "29.1.2" }, "scripts": { - "build": "pnpm --workspace-root nx build devextreme-scss", + "build": "pnpm nx build devextreme-scss", "lint": "stylelint scss/widgets", "test": "jest --no-coverage --runInBand --config=./tests/jest.config.json", - "watch": "pnpm --workspace-root nx run devextreme-scss --target=watch" + "watch": "pnpm nx run devextreme-scss --target=watch" }, "version": "26.1.2" } diff --git a/packages/devextreme-scss/project.json b/packages/devextreme-scss/project.json index f720e5dfbfdd..aa76b5144363 100644 --- a/packages/devextreme-scss/project.json +++ b/packages/devextreme-scss/project.json @@ -20,8 +20,8 @@ "executor": "nx:run-commands", "options": { "commands": [ - "pnpm --workspace-root nx clean:artifacts devextreme-scss", - "pnpm --workspace-root nx clean:bundles devextreme-scss" + "pnpm nx clean:artifacts devextreme-scss", + "pnpm nx clean:bundles devextreme-scss" ], "parallel": false } @@ -52,6 +52,8 @@ }, "inputs": [ "{projectRoot}/build/**/*", + "{projectRoot}/fonts/**/*", + "{projectRoot}/icons/**/*", "{projectRoot}/images/**/*", "{projectRoot}/scss/**/*" ], @@ -68,6 +70,8 @@ }, "inputs": [ "{projectRoot}/build/**/*", + "{projectRoot}/fonts/**/*", + "{projectRoot}/icons/**/*", "{projectRoot}/images/**/*", "{projectRoot}/scss/**/*" ], @@ -81,9 +85,8 @@ "executor": "nx:run-commands", "options": { "commands": [ - "pnpm --workspace-root nx clean devextreme-scss", - "pnpm --workspace-root nx build:themes devextreme-scss", - "pnpm --workspace-root nx copy:assets devextreme-scss" + "pnpm nx clean devextreme-scss", + "pnpm nx run-many --targets=build:themes,copy:assets --projects=devextreme-scss --parallel" ], "parallel": false }, @@ -106,9 +109,8 @@ "executor": "nx:run-commands", "options": { "commands": [ - "pnpm --workspace-root nx clean devextreme-scss", - "pnpm --workspace-root nx build:themes-dev devextreme-scss", - "pnpm --workspace-root nx copy:assets devextreme-scss" + "pnpm nx clean devextreme-scss", + "pnpm nx run-many --targets=build:themes-dev,copy:assets --projects=devextreme-scss --parallel" ], "parallel": false }, diff --git a/packages/devextreme/package.json b/packages/devextreme/package.json index 4fe4400be758..73cb71e17c7f 100644 --- a/packages/devextreme/package.json +++ b/packages/devextreme/package.json @@ -231,7 +231,7 @@ "build:testcafe": "cross-env DEVEXTREME_TEST_CI=TRUE BUILD_ESM_PACKAGE=true BUILD_TESTCAFE=TRUE gulp default", "build-npm-devextreme": "cross-env BUILD_ESM_PACKAGE=true gulp default", "build-dist": "cross-env BUILD_ESM_PACKAGE=true gulp default --uglify", - "build-themes": "pnpm --workspace-root nx build:themes devextreme-scss && pnpm --workspace-root nx copy:assets devextreme-scss", + "build-themes": "pnpm nx build:themes devextreme-scss && pnpm nx copy:assets devextreme-scss", "build:react": "gulp generate-react", "build:react:watch": "gulp generate-react-watch", "build:react:typescript": "gulp generate-react-typescript", From 227bb516ae4f6d71aec071b10947b05dbbd11927 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Tue, 19 May 2026 15:19:44 +0200 Subject: [PATCH 11/23] devextreme-scss build refactor --- packages/devextreme/package.json | 2 +- .../scss-assemble/scss-assemble.impl.ts | 23 +- .../src/executors/scss-build/executor.ts | 378 +----------------- .../executors/scss-build/scss-build.impl.ts | 378 ++++++++++++++++++ .../src/utils/scss-data-uri.spec.ts | 22 + .../src/utils/scss-data-uri.ts | 33 ++ 6 files changed, 442 insertions(+), 394 deletions(-) create mode 100644 packages/nx-infra-plugin/src/executors/scss-build/scss-build.impl.ts create mode 100644 packages/nx-infra-plugin/src/utils/scss-data-uri.spec.ts create mode 100644 packages/nx-infra-plugin/src/utils/scss-data-uri.ts diff --git a/packages/devextreme/package.json b/packages/devextreme/package.json index 73cb71e17c7f..7d36196e44ee 100644 --- a/packages/devextreme/package.json +++ b/packages/devextreme/package.json @@ -231,7 +231,7 @@ "build:testcafe": "cross-env DEVEXTREME_TEST_CI=TRUE BUILD_ESM_PACKAGE=true BUILD_TESTCAFE=TRUE gulp default", "build-npm-devextreme": "cross-env BUILD_ESM_PACKAGE=true gulp default", "build-dist": "cross-env BUILD_ESM_PACKAGE=true gulp default --uglify", - "build-themes": "pnpm nx build:themes devextreme-scss && pnpm nx copy:assets devextreme-scss", + "build-themes": "pnpm nx build devextreme-scss", "build:react": "gulp generate-react", "build:react:watch": "gulp generate-react-watch", "build:react:typescript": "gulp generate-react-typescript", diff --git a/packages/nx-infra-plugin/src/executors/scss-assemble/scss-assemble.impl.ts b/packages/nx-infra-plugin/src/executors/scss-assemble/scss-assemble.impl.ts index 3c41547c3547..cc1984f481ae 100644 --- a/packages/nx-infra-plugin/src/executors/scss-assemble/scss-assemble.impl.ts +++ b/packages/nx-infra-plugin/src/executors/scss-assemble/scss-assemble.impl.ts @@ -6,23 +6,16 @@ import { createExecutor } from '../../utils/create-executor'; import { toPosixPath } from '../../utils/path-resolver'; import { readFileText, writeFileText, ensureDir } from '../../utils/file-operations'; import { copyDirectory } from '../copy-files/copy-files.impl'; +import { + DATA_URI_SCSS_REGEX, + encodeDataUriForCssUrl, +} from '../../utils/scss-data-uri'; import { ScssAssembleExecutorSchema } from './schema'; -const DATA_URI_REGEX = /data-uri\((?:'(image\/svg\+xml;charset=UTF-8)',\s)?['"]?([^)'"]+)['"]?\)/g; - const SCSS_EXTENSIONS = new Set(['.scss', '.css']); -function encodeSvg(buffer: Buffer, svgEncoding?: string): string { - const encoding = svgEncoding ?? 'image/svg+xml;charset=UTF-8'; - return `"data:${encoding},${encodeURIComponent(buffer.toString())}"`; -} - -function encodeImage(buffer: Buffer, ext: string): string { - return `"data:image/${ext};base64,${buffer.toString('base64')}"`; -} - async function inlineDataUri(content: string, scssRoot: string): Promise { - const matches = [...content.matchAll(DATA_URI_REGEX)]; + const matches = [...content.matchAll(DATA_URI_SCSS_REGEX)]; if (matches.length === 0) return content; const replacements = new Map(); @@ -35,15 +28,13 @@ async function inlineDataUri(content: string, scssRoot: string): Promise const svgEncoding = match[1]; const fileName = match[2]; const filePath = path.resolve(scssRoot, fileName); - const ext = path.extname(filePath).slice(1); const buffer = await fs.readFile(filePath); - const escapedString = - ext === 'svg' ? encodeSvg(buffer, svgEncoding) : encodeImage(buffer, ext); + const escapedString = encodeDataUriForCssUrl(buffer, filePath, svgEncoding); replacements.set(matchStr, `url(${escapedString})`); }), ); - return content.replace(DATA_URI_REGEX, (match) => replacements.get(match) ?? match); + return content.replace(DATA_URI_SCSS_REGEX, (match) => replacements.get(match) ?? match); } async function copyScssWithInlineDataUri( diff --git a/packages/nx-infra-plugin/src/executors/scss-build/executor.ts b/packages/nx-infra-plugin/src/executors/scss-build/executor.ts index 041e1096bf81..8c0bdaed7166 100644 --- a/packages/nx-infra-plugin/src/executors/scss-build/executor.ts +++ b/packages/nx-infra-plugin/src/executors/scss-build/executor.ts @@ -1,377 +1 @@ -import { PromiseExecutor, logger } from '@nx/devkit'; -import * as fs from 'fs'; -import * as path from 'path'; -import { createRequire } from 'module'; -import { glob } from 'glob'; -import { ScssBuildExecutorSchema } from './schema'; -import { normalizeGlobPathForWindows, resolveProjectPath } from '../../utils/path-resolver'; -import { ensureDir, readFileText, writeFileText } from '../../utils/file-operations'; - -const DEFAULT_BUNDLES_DIR = './scss/bundles'; -const DEFAULT_CSS_OUTPUT_DIR = '../devextreme/artifacts/css'; -const DEFAULT_DEV_BUNDLE_NAMES = [ - 'light', - 'light.compact', - 'dark', - 'contrast', - 'material.blue.light', - 'material.blue.light.compact', - 'material.blue.dark', - 'fluent.blue.light', - 'fluent.blue.light.compact', - 'fluent.blue.dark', - 'fluent.saas.light', - 'fluent.saas.dark', -]; - -const EULA_URL = 'https://js.devexpress.com/Licensing/'; - -interface BuildDependencies { - sass: any; - postcss: any; - autoprefixer: (options?: { overrideBrowserslist?: string[] }) => any; - chokidar: { - watch: ( - paths: string | string[], - options?: Record, - ) => { - on: (event: string, handler: (...args: any[]) => void) => unknown; - close: () => Promise | void; - }; - }; - CleanCss: new (options: unknown) => { minify: (input: string) => { styles: string } }; - themeOptions: { getThemes: () => Array<[string, string, string, string?]> }; - cleanCssSanitizeOptions: unknown; - cleanCssDevOptions: unknown; - devextremeVersion: string; -} - -type MinifyProfile = 'all' | 'ci'; - -function resolveDataUri(filePath: string, svgEncoding?: string): string { - const ext = path.extname(filePath).replace('.', ''); - const data = fs.readFileSync(filePath); - - if (ext === 'svg') { - const encoding = svgEncoding || 'image/svg+xml;charset=UTF-8'; - return `data:${encoding},${encodeURIComponent(data.toString())}`; - } - - return `data:image/${ext};base64,${data.toString('base64')}`; -} - -/** - * Same shape as `packages/devextreme/build/gulp/license-header.txt` with - * `gulp-header` `commentType: '*'` (starLicense) — matches legacy Gulp output. - */ -function createStarLicenseHeader(fileName: string, version: string): string { - return [ - '/**', - `* DevExtreme (${fileName.replace(/\\/g, '/')})`, - `* Version: ${version}`, - `* Build date: ${new Date().toDateString()}`, - '*', - `* Copyright (c) 2012 - ${new Date().getFullYear()} Developer Express Inc. ALL RIGHTS RESERVED`, - `* Read about DevExtreme licensing here: ${EULA_URL}`, - '*/', - '', - ].join('\n'); -} - -function prependLicenseAndMoveCharsetFirst(minifiedCss: string, license: string): string { - const withLicense = `${license}${minifiedCss}`; - return withLicense.replace(/([\s\S]*)(@charset[^;]+;\s*)/, '$2$1'); -} - -function generateBundleName(theme: string, size: string, color: string, mode?: string): string { - return ( - 'dx' - + (theme === 'material' || theme === 'fluent' ? `.${theme}` : '') - + `.${color}` - + (mode ? `.${mode}` : '') - + (size === 'default' ? '' : '.compact') - + '.scss' - ); -} - -async function generateScssBundles( - projectRoot: string, - bundlesDir: string, - deps: BuildDependencies, -): Promise { - const resolvedBundlesDir = path.resolve(projectRoot, bundlesDir); - const buildDir = path.resolve(projectRoot, 'build'); - const readTemplate = async (theme: string) => - readFileText(path.join(buildDir, `bundle-template.${theme}.scss`)); - - await ensureDir(resolvedBundlesDir); - - const themes = deps.themeOptions.getThemes(); - for (const [theme, size, color, mode] of themes) { - const template = await readTemplate(theme); - const content = template - .replace('$COLOR', color) - .replace('$SIZE', size) - .replace('$MODE', mode || ''); - const fileName = generateBundleName(theme, size, color, mode); - await writeFileText(path.join(resolvedBundlesDir, fileName), content); - } - - const commonTemplate = await readTemplate('common'); - await writeFileText(path.join(resolvedBundlesDir, 'dx.common.scss'), commonTemplate); -} - -function loadDependencies(projectRoot: string): BuildDependencies { - const projectRequire = createRequire(path.join(projectRoot, 'package.json')); - const workspaceRequire = createRequire(path.join(projectRoot, '..', '..', 'package.json')); - - return { - sass: projectRequire('sass-embedded'), - postcss: workspaceRequire('postcss'), - autoprefixer: workspaceRequire('autoprefixer'), - chokidar: workspaceRequire('chokidar'), - CleanCss: workspaceRequire('clean-css'), - themeOptions: projectRequire(path.resolve(projectRoot, 'build/theme-options.cjs')) as { - getThemes: () => Array<[string, string, string, string?]>; - }, - cleanCssSanitizeOptions: projectRequire( - path.resolve(projectRoot, 'build/clean-css-options.json'), - ), - cleanCssDevOptions: workspaceRequire( - path.resolve(projectRoot, '../devextreme-themebuilder/src/data/clean-css-options.json'), - ), - devextremeVersion: workspaceRequire(path.resolve(projectRoot, '../devextreme/package.json')) - .version, - }; -} - -function normalizeBundlesOption(bundles?: string[] | string): string[] | undefined { - if (!bundles) { - return undefined; - } - - if (Array.isArray(bundles)) { - return bundles; - } - - return bundles - .split(',') - .map((bundle) => bundle.trim()) - .filter(Boolean); -} - -function resolveSourceFiles( - projectRoot: string, - options: ScssBuildExecutorSchema, -): Promise { - const bundlesDir = path.resolve(projectRoot, options.bundlesDir || DEFAULT_BUNDLES_DIR); - - if (options.mode === 'ci') { - const bundleNames = options.devBundles || DEFAULT_DEV_BUNDLE_NAMES; - return Promise.resolve(bundleNames.map((name) => path.join(bundlesDir, `dx.${name}.scss`))); - } - - const pattern = normalizeGlobPathForWindows(path.join(bundlesDir, 'dx.*.scss')); - return glob(pattern, { nodir: true }); -} - -function createDataUriFunction(projectRoot: string, sass: any): (args: any[]) => any { - return (args: any[]) => { - const argList = args[0].asList; - const hasEncoding = argList.size === 2; - const encoding = hasEncoding ? argList.get(0).assertString().text : undefined; - const url = argList.get(hasEncoding ? 1 : 0).assertString().text; - const absolutePath = path.resolve(projectRoot, url); - - const dataUri = resolveDataUri(absolutePath, encoding); - return new sass.SassString(`url("${dataUri}")`, { quotes: false }); - }; -} - -async function compileFile( - sourceFile: string, - outputDir: string, - minifyProfile: MinifyProfile, - deps: BuildDependencies, - projectRoot: string, -): Promise { - const dataUriFunction = createDataUriFunction(projectRoot, deps.sass); - const compiled = deps.sass.compile(sourceFile, { - functions: { - 'data-uri($args...)': dataUriFunction, - }, - }); - - const postcssFactory = (deps.postcss as unknown as { default?: any }).default || deps.postcss; - const prefixed = await postcssFactory([deps.autoprefixer()]).process(compiled.css, { - from: sourceFile, - }); - - const minifierOptions = - minifyProfile === 'ci' ? deps.cleanCssDevOptions : deps.cleanCssSanitizeOptions; - const minifier = new deps.CleanCss(minifierOptions); - const minified = minifier.minify(prefixed.css).styles; - - const outFileName = path.basename(sourceFile, '.scss') + '.css'; - const license = createStarLicenseHeader(outFileName, deps.devextremeVersion); - const withHeader = prependLicenseAndMoveCharsetFirst(minified, license); - await writeFileText(path.join(outputDir, outFileName), withHeader); -} - -async function copyAssets(projectRoot: string, cssOutputDir: string): Promise { - const fontsFrom = path.resolve(projectRoot, 'fonts'); - const iconsFrom = path.resolve(projectRoot, 'icons'); - const fontsTo = path.resolve(cssOutputDir, 'fonts'); - const iconsTo = path.resolve(cssOutputDir, 'icons'); - - if (fs.existsSync(fontsFrom)) { - await ensureDir(fontsTo); - fs.cpSync(fontsFrom, fontsTo, { recursive: true }); - } - - if (fs.existsSync(iconsFrom)) { - await ensureDir(iconsTo); - fs.cpSync(iconsFrom, iconsTo, { recursive: true }); - } -} - -function resolveSourcesByBundleNames( - projectRoot: string, - bundlesDir: string, - bundleNames: string[], -): string[] { - const resolvedBundlesDir = path.resolve(projectRoot, bundlesDir); - const sources: string[] = []; - - for (const bundleName of bundleNames) { - const source = path.join(resolvedBundlesDir, `dx.${bundleName}.scss`); - if (fs.existsSync(source)) { - sources.push(source); - } else { - logger.warn(`${source} file does not exist`); - } - } - - return sources; -} - -function getWatchBundleNames(options: ScssBuildExecutorSchema): string[] { - const explicitBundles = normalizeBundlesOption(options.bundles); - if (explicitBundles && explicitBundles.length > 0) { - return explicitBundles; - } - - return options.devBundles || DEFAULT_DEV_BUNDLE_NAMES; -} - -async function runSingleBuild( - projectRoot: string, - options: ScssBuildExecutorSchema, - deps: BuildDependencies, -): Promise { - const bundlesDir = options.bundlesDir || DEFAULT_BUNDLES_DIR; - const cssOutputDir = path.resolve(projectRoot, options.cssOutputDir || DEFAULT_CSS_OUTPUT_DIR); - - await generateScssBundles(projectRoot, bundlesDir, deps); - await ensureDir(cssOutputDir); - - const sources = await resolveSourceFiles(projectRoot, options); - const existingSources = sources.filter((source) => fs.existsSync(source)); - const minifyProfile: MinifyProfile = options.mode === 'ci' ? 'ci' : 'all'; - - for (const source of existingSources) { - logger.verbose(`Compiling ${source}`); - await compileFile(source, cssOutputDir, minifyProfile, deps, projectRoot); - } -} - -async function runWatchBuild( - projectRoot: string, - options: ScssBuildExecutorSchema, - deps: BuildDependencies, -): Promise<{ success: boolean }> { - const bundlesDir = options.bundlesDir || DEFAULT_BUNDLES_DIR; - const cssOutputDir = path.resolve(projectRoot, options.cssOutputDir || DEFAULT_CSS_OUTPUT_DIR); - const watchDir = path.resolve(projectRoot, 'scss'); - const watchBundleNames = getWatchBundleNames(options); - const minifyProfile: MinifyProfile = options.mode === 'ci' ? 'ci' : 'all'; - - const rebuild = async (): Promise => { - await generateScssBundles(projectRoot, bundlesDir, deps); - await ensureDir(cssOutputDir); - - const sources = resolveSourcesByBundleNames(projectRoot, bundlesDir, watchBundleNames); - for (const source of sources) { - await compileFile(source, cssOutputDir, minifyProfile, deps, projectRoot); - } - - await copyAssets(projectRoot, cssOutputDir); - }; - - await rebuild(); - logger.info('scss-build watch mode is watching for changes...'); - - return await new Promise<{ success: boolean }>((resolve) => { - let timer: NodeJS.Timeout | undefined; - let busy = false; - - const scheduleRebuild = () => { - if (timer) { - clearTimeout(timer); - } - - timer = setTimeout(async () => { - if (busy) { - return; - } - - busy = true; - try { - await rebuild(); - logger.info('scss-build watch: rebuild complete'); - } catch (error) { - const message = error instanceof Error ? error.message : String(error); - logger.error(`scss-build watch rebuild failed: ${message}`); - } finally { - busy = false; - } - }, 200); - }; - - const watcher = deps.chokidar.watch(path.join(watchDir, '**/*.scss'), { - ignoreInitial: true, - }); - watcher.on('all', scheduleRebuild); - - const stopWatcher = () => { - void watcher.close(); - if (timer) { - clearTimeout(timer); - } - resolve({ success: true }); - }; - - process.once('SIGINT', stopWatcher); - process.once('SIGTERM', stopWatcher); - }); -} - -const runExecutor: PromiseExecutor = async (options, context) => { - const projectRoot = resolveProjectPath(context); - - try { - const deps = loadDependencies(projectRoot); - if (options.watch) { - return await runWatchBuild(projectRoot, options, deps); - } - - await runSingleBuild(projectRoot, options, deps); - return { success: true }; - } catch (error) { - const message = error instanceof Error ? error.message : String(error); - logger.error(`SCSS build failed: ${message}`); - return { success: false }; - } -}; - -export default runExecutor; +export { default } from './scss-build.impl'; diff --git a/packages/nx-infra-plugin/src/executors/scss-build/scss-build.impl.ts b/packages/nx-infra-plugin/src/executors/scss-build/scss-build.impl.ts new file mode 100644 index 000000000000..1440e3784af5 --- /dev/null +++ b/packages/nx-infra-plugin/src/executors/scss-build/scss-build.impl.ts @@ -0,0 +1,378 @@ +import { logger } from '@nx/devkit'; +import * as fs from 'fs'; +import * as path from 'path'; +import { createRequire } from 'module'; +import { glob } from 'glob'; +import { createExecutor } from '../../utils/create-executor'; +import { normalizeGlobPathForWindows } from '../../utils/path-resolver'; +import { ensureDir, exists, readFileText, writeFileText } from '../../utils/file-operations'; +import { encodeDataUriContent } from '../../utils/scss-data-uri'; +import { DEFAULT_EULA_URL } from '../add-license-headers/defaults'; +import { copyDirectory } from '../copy-files/copy-files.impl'; +import { ScssBuildExecutorSchema } from './schema'; + +const DEFAULT_BUNDLES_DIR = './scss/bundles'; +const DEFAULT_CSS_OUTPUT_DIR = '../devextreme/artifacts/css'; +const DEFAULT_DEV_BUNDLE_NAMES = [ + 'light', + 'light.compact', + 'dark', + 'contrast', + 'material.blue.light', + 'material.blue.light.compact', + 'material.blue.dark', + 'fluent.blue.light', + 'fluent.blue.light.compact', + 'fluent.blue.dark', + 'fluent.saas.light', + 'fluent.saas.dark', +]; + +interface BuildDependencies { + sass: any; + postcss: any; + autoprefixer: (options?: { overrideBrowserslist?: string[] }) => any; + chokidar: { + watch: ( + paths: string | string[], + options?: Record, + ) => { + on: (event: string, handler: (...args: any[]) => void) => unknown; + close: () => Promise | void; + }; + }; + CleanCss: new (options: unknown) => { minify: (input: string) => { styles: string } }; + themeOptions: { getThemes: () => Array<[string, string, string, string?]> }; + cleanCssSanitizeOptions: unknown; + cleanCssDevOptions: unknown; + devextremeVersion: string; +} + +type MinifyProfile = 'all' | 'ci'; + +interface ResolvedScssBuild { + projectRoot: string; + options: ScssBuildExecutorSchema; + deps: BuildDependencies; +} + +function readFileDataUri(filePath: string, svgEncoding?: string): string { + const buffer = fs.readFileSync(filePath); + return encodeDataUriContent(buffer, filePath, svgEncoding); +} + +/** + * Same shape as `packages/devextreme/build/gulp/license-header.txt` with + * `gulp-header` `commentType: '*'` (starLicense) — matches legacy Gulp output. + */ +function createStarLicenseHeader(fileName: string, version: string): string { + return [ + '/**', + `* DevExtreme (${fileName.replace(/\\/g, '/')})`, + `* Version: ${version}`, + `* Build date: ${new Date().toDateString()}`, + '*', + `* Copyright (c) 2012 - ${new Date().getFullYear()} Developer Express Inc. ALL RIGHTS RESERVED`, + `* Read about DevExtreme licensing here: ${DEFAULT_EULA_URL}`, + '*/', + '', + ].join('\n'); +} + +function prependLicenseAndMoveCharsetFirst(minifiedCss: string, license: string): string { + const withLicense = `${license}${minifiedCss}`; + return withLicense.replace(/([\s\S]*)(@charset[^;]+;\s*)/, '$2$1'); +} + +function generateBundleName(theme: string, size: string, color: string, mode?: string): string { + return ( + 'dx' + + (theme === 'material' || theme === 'fluent' ? `.${theme}` : '') + + `.${color}` + + (mode ? `.${mode}` : '') + + (size === 'default' ? '' : '.compact') + + '.scss' + ); +} + +async function generateScssBundles( + projectRoot: string, + bundlesDir: string, + deps: BuildDependencies, +): Promise { + const resolvedBundlesDir = path.resolve(projectRoot, bundlesDir); + const buildDir = path.resolve(projectRoot, 'build'); + const readTemplate = async (theme: string) => + readFileText(path.join(buildDir, `bundle-template.${theme}.scss`)); + + await ensureDir(resolvedBundlesDir); + + const themes = deps.themeOptions.getThemes(); + for (const [theme, size, color, mode] of themes) { + const template = await readTemplate(theme); + const content = template + .replace('$COLOR', color) + .replace('$SIZE', size) + .replace('$MODE', mode || ''); + const fileName = generateBundleName(theme, size, color, mode); + await writeFileText(path.join(resolvedBundlesDir, fileName), content); + } + + const commonTemplate = await readTemplate('common'); + await writeFileText(path.join(resolvedBundlesDir, 'dx.common.scss'), commonTemplate); +} + +function loadDependencies(projectRoot: string): BuildDependencies { + const projectRequire = createRequire(path.join(projectRoot, 'package.json')); + const workspaceRequire = createRequire(path.join(projectRoot, '..', '..', 'package.json')); + + return { + sass: projectRequire('sass-embedded'), + postcss: workspaceRequire('postcss'), + autoprefixer: workspaceRequire('autoprefixer'), + chokidar: workspaceRequire('chokidar'), + CleanCss: workspaceRequire('clean-css'), + themeOptions: projectRequire(path.resolve(projectRoot, 'build/theme-options.cjs')) as { + getThemes: () => Array<[string, string, string, string?]>; + }, + cleanCssSanitizeOptions: projectRequire( + path.resolve(projectRoot, 'build/clean-css-options.json'), + ), + cleanCssDevOptions: workspaceRequire( + path.resolve(projectRoot, '../devextreme-themebuilder/src/data/clean-css-options.json'), + ), + devextremeVersion: workspaceRequire(path.resolve(projectRoot, '../devextreme/package.json')) + .version, + }; +} + +function normalizeBundlesOption(bundles?: string[] | string): string[] | undefined { + if (!bundles) { + return undefined; + } + + if (Array.isArray(bundles)) { + return bundles; + } + + return bundles + .split(',') + .map((bundle) => bundle.trim()) + .filter(Boolean); +} + +function resolveSourceFiles( + projectRoot: string, + options: ScssBuildExecutorSchema, +): Promise { + const bundlesDir = path.resolve(projectRoot, options.bundlesDir || DEFAULT_BUNDLES_DIR); + + if (options.mode === 'ci') { + const bundleNames = options.devBundles || DEFAULT_DEV_BUNDLE_NAMES; + return Promise.resolve(bundleNames.map((name) => path.join(bundlesDir, `dx.${name}.scss`))); + } + + const pattern = normalizeGlobPathForWindows(path.join(bundlesDir, 'dx.*.scss')); + return glob(pattern, { nodir: true }); +} + +function createDataUriFunction(projectRoot: string, sass: any): (args: any[]) => any { + return (args: any[]) => { + const argList = args[0].asList; + const hasEncoding = argList.size === 2; + const encoding = hasEncoding ? argList.get(0).assertString().text : undefined; + const url = argList.get(hasEncoding ? 1 : 0).assertString().text; + const absolutePath = path.resolve(projectRoot, url); + + const dataUri = readFileDataUri(absolutePath, encoding); + return new sass.SassString(`url("${dataUri}")`, { quotes: false }); + }; +} + +async function compileFile( + sourceFile: string, + outputDir: string, + minifyProfile: MinifyProfile, + deps: BuildDependencies, + projectRoot: string, +): Promise { + const dataUriFunction = createDataUriFunction(projectRoot, deps.sass); + const compiled = deps.sass.compile(sourceFile, { + functions: { + 'data-uri($args...)': dataUriFunction, + }, + }); + + const postcssFactory = (deps.postcss as unknown as { default?: any }).default || deps.postcss; + const prefixed = await postcssFactory([deps.autoprefixer()]).process(compiled.css, { + from: sourceFile, + }); + + const minifierOptions = + minifyProfile === 'ci' ? deps.cleanCssDevOptions : deps.cleanCssSanitizeOptions; + const minifier = new deps.CleanCss(minifierOptions); + const minified = minifier.minify(prefixed.css).styles; + + const outFileName = path.basename(sourceFile, '.scss') + '.css'; + const license = createStarLicenseHeader(outFileName, deps.devextremeVersion); + const withHeader = prependLicenseAndMoveCharsetFirst(minified, license); + await writeFileText(path.join(outputDir, outFileName), withHeader); +} + +async function copyThemeAssets(projectRoot: string, cssOutputDir: string): Promise { + const fontsFrom = path.resolve(projectRoot, 'fonts'); + const iconsFrom = path.resolve(projectRoot, 'icons'); + const fontsTo = path.resolve(cssOutputDir, 'fonts'); + const iconsTo = path.resolve(cssOutputDir, 'icons'); + + await Promise.all([ + (async () => { + if (await exists(fontsFrom)) { + await copyDirectory(fontsFrom, fontsTo); + } + })(), + (async () => { + if (await exists(iconsFrom)) { + await copyDirectory(iconsFrom, iconsTo); + } + })(), + ]); +} + +function resolveSourcesByBundleNames( + projectRoot: string, + bundlesDir: string, + bundleNames: string[], +): string[] { + const resolvedBundlesDir = path.resolve(projectRoot, bundlesDir); + const sources: string[] = []; + + for (const bundleName of bundleNames) { + const source = path.join(resolvedBundlesDir, `dx.${bundleName}.scss`); + if (fs.existsSync(source)) { + sources.push(source); + } else { + logger.warn(`${source} file does not exist`); + } + } + + return sources; +} + +function getWatchBundleNames(options: ScssBuildExecutorSchema): string[] { + const explicitBundles = normalizeBundlesOption(options.bundles); + if (explicitBundles && explicitBundles.length > 0) { + return explicitBundles; + } + + return options.devBundles || DEFAULT_DEV_BUNDLE_NAMES; +} + +async function runSingleBuild( + projectRoot: string, + options: ScssBuildExecutorSchema, + deps: BuildDependencies, +): Promise { + const bundlesDir = options.bundlesDir || DEFAULT_BUNDLES_DIR; + const cssOutputDir = path.resolve(projectRoot, options.cssOutputDir || DEFAULT_CSS_OUTPUT_DIR); + + await generateScssBundles(projectRoot, bundlesDir, deps); + await ensureDir(cssOutputDir); + + const sources = await resolveSourceFiles(projectRoot, options); + const existingSources = sources.filter((source) => fs.existsSync(source)); + const minifyProfile: MinifyProfile = options.mode === 'ci' ? 'ci' : 'all'; + + for (const source of existingSources) { + logger.verbose(`Compiling ${source}`); + await compileFile(source, cssOutputDir, minifyProfile, deps, projectRoot); + } +} + +async function runWatchBuild( + projectRoot: string, + options: ScssBuildExecutorSchema, + deps: BuildDependencies, +): Promise { + const bundlesDir = options.bundlesDir || DEFAULT_BUNDLES_DIR; + const cssOutputDir = path.resolve(projectRoot, options.cssOutputDir || DEFAULT_CSS_OUTPUT_DIR); + const watchDir = path.resolve(projectRoot, 'scss'); + const watchBundleNames = getWatchBundleNames(options); + const minifyProfile: MinifyProfile = options.mode === 'ci' ? 'ci' : 'all'; + + const rebuild = async (): Promise => { + await generateScssBundles(projectRoot, bundlesDir, deps); + await ensureDir(cssOutputDir); + + const sources = resolveSourcesByBundleNames(projectRoot, bundlesDir, watchBundleNames); + for (const source of sources) { + await compileFile(source, cssOutputDir, minifyProfile, deps, projectRoot); + } + + await copyThemeAssets(projectRoot, cssOutputDir); + }; + + await rebuild(); + logger.info('scss-build watch mode is watching for changes...'); + + await new Promise((resolve) => { + let timer: NodeJS.Timeout | undefined; + let busy = false; + + const scheduleRebuild = () => { + if (timer) { + clearTimeout(timer); + } + + timer = setTimeout(async () => { + if (busy) { + return; + } + + busy = true; + try { + await rebuild(); + logger.info('scss-build watch: rebuild complete'); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + logger.error(`scss-build watch rebuild failed: ${message}`); + } finally { + busy = false; + } + }, 200); + }; + + const watcher = deps.chokidar.watch(path.join(watchDir, '**/*.scss'), { + ignoreInitial: true, + }); + watcher.on('all', scheduleRebuild); + + const stopWatcher = () => { + void watcher.close(); + if (timer) { + clearTimeout(timer); + } + resolve(); + }; + + process.once('SIGINT', stopWatcher); + process.once('SIGTERM', stopWatcher); + }); +} + +export default createExecutor({ + name: 'ScssBuild', + resolve: (options, { projectRoot }) => ({ + projectRoot, + options, + deps: loadDependencies(projectRoot), + }), + run: async ({ projectRoot, options, deps }) => { + if (options.watch) { + await runWatchBuild(projectRoot, options, deps); + return; + } + + await runSingleBuild(projectRoot, options, deps); + }, +}); diff --git a/packages/nx-infra-plugin/src/utils/scss-data-uri.spec.ts b/packages/nx-infra-plugin/src/utils/scss-data-uri.spec.ts new file mode 100644 index 000000000000..4bcc55bcbcd0 --- /dev/null +++ b/packages/nx-infra-plugin/src/utils/scss-data-uri.spec.ts @@ -0,0 +1,22 @@ +import { encodeDataUriContent, encodeDataUriForCssUrl } from './scss-data-uri'; + +describe('scss-data-uri', () => { + it('encodes svg as utf-8 data uri', () => { + const buffer = Buffer.from(''); + expect(encodeDataUriContent(buffer, 'icon.svg')).toBe( + 'data:image/svg+xml;charset=UTF-8,%3Csvg%3E%3C%2Fsvg%3E', + ); + }); + + it('encodes raster images as base64', () => { + const buffer = Buffer.from('png-bytes'); + expect(encodeDataUriContent(buffer, 'icon.png')).toBe( + 'data:image/png;base64,cG5nLWJ5dGVz', + ); + }); + + it('wraps payload for css url() replacement', () => { + const buffer = Buffer.from('x'); + expect(encodeDataUriForCssUrl(buffer, 'a.png')).toBe('"data:image/png;base64,eA=="'); + }); +}); diff --git a/packages/nx-infra-plugin/src/utils/scss-data-uri.ts b/packages/nx-infra-plugin/src/utils/scss-data-uri.ts new file mode 100644 index 000000000000..73d49008a60c --- /dev/null +++ b/packages/nx-infra-plugin/src/utils/scss-data-uri.ts @@ -0,0 +1,33 @@ +import * as path from 'path'; + +/** + * Unquoted `data:` URI payload (used inside Sass/CSS `url("...")`). + */ +export function encodeDataUriContent( + buffer: Buffer, + filePathOrExt: string, + svgEncoding?: string, +): string { + const ext = path.extname(filePathOrExt).replace('.', '').toLowerCase(); + + if (ext === 'svg') { + const encoding = svgEncoding ?? 'image/svg+xml;charset=UTF-8'; + return `data:${encoding},${encodeURIComponent(buffer.toString())}`; + } + + return `data:image/${ext};base64,${buffer.toString('base64')}`; +} + +/** + * Quoted data URI for `url(...)` replacement when inlining in assembled SCSS sources. + */ +export function encodeDataUriForCssUrl( + buffer: Buffer, + filePathOrExt: string, + svgEncoding?: string, +): string { + return `"${encodeDataUriContent(buffer, filePathOrExt, svgEncoding)}"`; +} + +export const DATA_URI_SCSS_REGEX = + /data-uri\((?:'(image\/svg\+xml;charset=UTF-8)',\s)?['"]?([^)'"]+)['"]?\)/g; From dfb4d000b7154a624ae7b3eabab14fdf5c013281 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Tue, 26 May 2026 22:04:28 +0200 Subject: [PATCH 12/23] fix lint --- .../src/executors/scss-assemble/scss-assemble.impl.ts | 5 +---- packages/nx-infra-plugin/src/utils/scss-data-uri.spec.ts | 4 +--- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/nx-infra-plugin/src/executors/scss-assemble/scss-assemble.impl.ts b/packages/nx-infra-plugin/src/executors/scss-assemble/scss-assemble.impl.ts index cc1984f481ae..b49778435343 100644 --- a/packages/nx-infra-plugin/src/executors/scss-assemble/scss-assemble.impl.ts +++ b/packages/nx-infra-plugin/src/executors/scss-assemble/scss-assemble.impl.ts @@ -6,10 +6,7 @@ import { createExecutor } from '../../utils/create-executor'; import { toPosixPath } from '../../utils/path-resolver'; import { readFileText, writeFileText, ensureDir } from '../../utils/file-operations'; import { copyDirectory } from '../copy-files/copy-files.impl'; -import { - DATA_URI_SCSS_REGEX, - encodeDataUriForCssUrl, -} from '../../utils/scss-data-uri'; +import { DATA_URI_SCSS_REGEX, encodeDataUriForCssUrl } from '../../utils/scss-data-uri'; import { ScssAssembleExecutorSchema } from './schema'; const SCSS_EXTENSIONS = new Set(['.scss', '.css']); diff --git a/packages/nx-infra-plugin/src/utils/scss-data-uri.spec.ts b/packages/nx-infra-plugin/src/utils/scss-data-uri.spec.ts index 4bcc55bcbcd0..a33abae0abaf 100644 --- a/packages/nx-infra-plugin/src/utils/scss-data-uri.spec.ts +++ b/packages/nx-infra-plugin/src/utils/scss-data-uri.spec.ts @@ -10,9 +10,7 @@ describe('scss-data-uri', () => { it('encodes raster images as base64', () => { const buffer = Buffer.from('png-bytes'); - expect(encodeDataUriContent(buffer, 'icon.png')).toBe( - 'data:image/png;base64,cG5nLWJ5dGVz', - ); + expect(encodeDataUriContent(buffer, 'icon.png')).toBe('data:image/png;base64,cG5nLWJ5dGVz'); }); it('wraps payload for css url() replacement', () => { From cd46d19df6f7c88af8c61d63e1c5f8627b8ddb03 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Wed, 27 May 2026 13:46:16 +0200 Subject: [PATCH 13/23] fix review notes --- .../executors/scss-build/scss-build.impl.ts | 40 ++++++++++++------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/packages/nx-infra-plugin/src/executors/scss-build/scss-build.impl.ts b/packages/nx-infra-plugin/src/executors/scss-build/scss-build.impl.ts index 1440e3784af5..5cfb9b5741a9 100644 --- a/packages/nx-infra-plugin/src/executors/scss-build/scss-build.impl.ts +++ b/packages/nx-infra-plugin/src/executors/scss-build/scss-build.impl.ts @@ -318,27 +318,37 @@ async function runWatchBuild( await new Promise((resolve) => { let timer: NodeJS.Timeout | undefined; let busy = false; + let pending = false; + + const runRebuild = async (): Promise => { + if (busy) { + pending = true; + return; + } + + busy = true; + try { + await rebuild(); + logger.info('scss-build watch: rebuild complete'); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + logger.error(`scss-build watch rebuild failed: ${message}`); + } finally { + busy = false; + if (pending) { + pending = false; + void runRebuild(); + } + } + }; const scheduleRebuild = () => { if (timer) { clearTimeout(timer); } - timer = setTimeout(async () => { - if (busy) { - return; - } - - busy = true; - try { - await rebuild(); - logger.info('scss-build watch: rebuild complete'); - } catch (error) { - const message = error instanceof Error ? error.message : String(error); - logger.error(`scss-build watch rebuild failed: ${message}`); - } finally { - busy = false; - } + timer = setTimeout(() => { + void runRebuild(); }, 200); }; From 24d33e68289be695751768c5c525d2d99e6b2908 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Wed, 3 Jun 2026 11:07:24 +0200 Subject: [PATCH 14/23] update lock --- pnpm-lock.yaml | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 72abb51f647d..bd44e6e4a5a4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2128,33 +2128,6 @@ importers: clean-css: specifier: 5.3.3 version: 5.3.3 - del: - specifier: 2.2.2 - version: 2.2.2 - gulp: - specifier: 4.0.2 - version: 4.0.2 - gulp-autoprefixer: - specifier: 10.0.0 - version: 10.0.0(gulp@4.0.2) - gulp-cache: - specifier: 1.1.3 - version: 1.1.3 - gulp-plumber: - specifier: 1.2.1 - version: 1.2.1 - gulp-replace: - specifier: 0.6.1 - version: 0.6.1 - gulp-sass: - specifier: 6.0.1 - version: 6.0.1 - gulp-shell: - specifier: 0.8.0 - version: 0.8.0 - minimist: - specifier: 1.2.8 - version: 1.2.8 sass-embedded: specifier: 1.93.3 version: 1.93.3 @@ -2167,9 +2140,6 @@ importers: stylelint-scss: specifier: 6.10.0 version: 6.10.0(stylelint@16.22.0(typescript@5.9.3)) - through2: - specifier: 2.0.5 - version: 2.0.5 ts-jest: specifier: 29.1.2 version: 29.1.2(@babel/core@7.29.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.0))(jest@30.2.0(@types/node@25.7.0)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.7.0)(typescript@5.9.3)))(typescript@5.9.3) From e0755d99b67e64f3c43806cee05bba238511029b Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Wed, 10 Jun 2026 16:25:25 +0200 Subject: [PATCH 15/23] remove comment --- .../src/executors/scss-build/scss-build.impl.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/nx-infra-plugin/src/executors/scss-build/scss-build.impl.ts b/packages/nx-infra-plugin/src/executors/scss-build/scss-build.impl.ts index 5cfb9b5741a9..1c30231d5128 100644 --- a/packages/nx-infra-plugin/src/executors/scss-build/scss-build.impl.ts +++ b/packages/nx-infra-plugin/src/executors/scss-build/scss-build.impl.ts @@ -61,10 +61,6 @@ function readFileDataUri(filePath: string, svgEncoding?: string): string { return encodeDataUriContent(buffer, filePath, svgEncoding); } -/** - * Same shape as `packages/devextreme/build/gulp/license-header.txt` with - * `gulp-header` `commentType: '*'` (starLicense) — matches legacy Gulp output. - */ function createStarLicenseHeader(fileName: string, version: string): string { return [ '/**', From d6f1421172fc80b242a095a923c73729114e5f4d Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Thu, 18 Jun 2026 16:29:43 +0200 Subject: [PATCH 16/23] add deps --- package.json | 2 + pnpm-lock.yaml | 677 +++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 605 insertions(+), 74 deletions(-) diff --git a/package.json b/package.json index 787150ad6702..1f93c1d56bfe 100644 --- a/package.json +++ b/package.json @@ -38,8 +38,10 @@ "@types/tar-fs": "2.0.4", "@types/yargs": "17.0.35", "@vue/shared": "3.4.27", + "autoprefixer": "~10.5.0", "axe-core": "catalog:", "cheerio": "1.2.0", + "chokidar": "~5.0.0", "codelyzer": "6.0.2", "devextreme-internal-tools": "catalog:tools", "devextreme-metadata": "workspace:*", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f1401d7be791..17325cb4e581 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -228,12 +228,18 @@ importers: '@vue/shared': specifier: 3.4.27 version: 3.4.27 + autoprefixer: + specifier: 10.5.0 + version: 10.5.0(postcss@8.5.10) axe-core: specifier: 'catalog:' version: 4.11.3 cheerio: specifier: 1.2.0 version: 1.2.0 + chokidar: + specifier: 5.0.0 + version: 5.0.0 codelyzer: specifier: 6.0.2 version: 6.0.2(@angular/compiler@21.2.15)(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.1))(tslint@6.1.3(typescript@5.9.3)) @@ -1006,7 +1012,7 @@ importers: version: 9.39.4(jiti@2.6.1) eslint-config-devextreme: specifier: 'catalog:' - version: 1.1.10(126991c980e575e73989a19e163db52c) + version: 1.1.10(ae8e86e33bb8f35b25c20474e34e4557) eslint-plugin-i18n: specifier: 'catalog:' version: 2.4.0 @@ -1078,7 +1084,7 @@ importers: version: 9.39.4(jiti@2.6.1) eslint-config-devextreme: specifier: 'catalog:' - version: 1.1.10(e8375d2d50fc1f6049e8cbb402fc51f0) + version: 1.1.10(27ed2754bbdc28fa18814d18b0630f40) eslint-migration-utils: specifier: workspace:* version: link:../../packages/eslint-migration-utils @@ -1295,10 +1301,10 @@ importers: devDependencies: '@analogjs/vite-plugin-angular': specifier: 1.22.5 - version: 1.22.5(@angular-devkit/build-angular@20.3.24(6cad9e8a55956960812b688933b0348b))(@angular/build@20.3.24(bdd7fe88370f70627ad553f473ae7c14)) + version: 1.22.5(@angular-devkit/build-angular@20.3.24(f806be40114e43f6d82d8cfbeea1241b))(@angular/build@20.3.24(bdd7fe88370f70627ad553f473ae7c14)) '@angular-devkit/build-angular': specifier: catalog:angular - version: 20.3.24(6cad9e8a55956960812b688933b0348b) + version: 20.3.24(f806be40114e43f6d82d8cfbeea1241b) '@angular/cli': specifier: catalog:angular version: 20.3.24(@types/node@25.7.0)(chokidar@4.0.3) @@ -1946,7 +1952,7 @@ importers: version: 9.39.4(jiti@2.6.1) eslint-config-devextreme: specifier: 'catalog:' - version: 1.1.10(e6a242bbff638c5d1de6ac61f88210f5) + version: 1.1.10(035a27461ca62d66425295e10c387bbc) eslint-migration-utils: specifier: workspace:* version: link:../eslint-migration-utils @@ -2731,6 +2737,7 @@ packages: '@angular/animations@21.2.15': resolution: {integrity: sha512-Z8AsLTwc++Fcu0fJnclAF9zMfumAd5KXrwtSdyECqLpqd+lEmmsOpeOl6P7loqdDz99KYh/8UF4eJxdMvnsaKw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + deprecated: '@angular/animations is deprecated. Use `animate.enter` and `animate.leave` instead. For more information see: https://v22.angular.dev/guide/animations.' peerDependencies: '@angular/core': 21.2.15 @@ -2927,6 +2934,7 @@ packages: '@angular/platform-browser-dynamic@20.3.21': resolution: {integrity: sha512-q45qofKVD1ijYlKmbSE5af39cq93wiiUZJmOLqnYfZ6IwHUE8fLbRMVHDNlY4kjO01dZRgBE0vMdwWp5+mEGIw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + deprecated: '@angular/platform-browser-dynamic is deprecated. Use `@angular/platform-browser` instead.' peerDependencies: '@angular/common': 20.3.21 '@angular/compiler': 20.3.21 @@ -2936,6 +2944,7 @@ packages: '@angular/platform-browser-dynamic@21.2.15': resolution: {integrity: sha512-3xvlWLZlsWjPyJFGatOOsod/f5AFjmSUDoOXo0zsr2ckHc4TxbDTnkLULhRSWv6m68fKOdQb8Si8rI15gC5yqA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + deprecated: '@angular/platform-browser-dynamic is deprecated. Use `@angular/platform-browser` instead.' peerDependencies: '@angular/common': 21.2.15 '@angular/compiler': 21.2.15 @@ -18684,12 +18693,12 @@ snapshots: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 - '@analogjs/vite-plugin-angular@1.22.5(@angular-devkit/build-angular@20.3.24(6cad9e8a55956960812b688933b0348b))(@angular/build@20.3.24(bdd7fe88370f70627ad553f473ae7c14))': + '@analogjs/vite-plugin-angular@1.22.5(@angular-devkit/build-angular@20.3.24(f806be40114e43f6d82d8cfbeea1241b))(@angular/build@20.3.24(bdd7fe88370f70627ad553f473ae7c14))': dependencies: ts-morph: 21.0.1 vfile: 6.0.3 optionalDependencies: - '@angular-devkit/build-angular': 20.3.24(6cad9e8a55956960812b688933b0348b) + '@angular-devkit/build-angular': 20.3.24(f806be40114e43f6d82d8cfbeea1241b) '@angular/build': 20.3.24(bdd7fe88370f70627ad553f473ae7c14) '@angular-devkit/architect@0.2003.24(chokidar@4.0.3)': @@ -18796,7 +18805,7 @@ snapshots: - webpack-cli - yaml - '@angular-devkit/build-angular@20.3.24(6cad9e8a55956960812b688933b0348b)': + '@angular-devkit/build-angular@20.3.24(f806be40114e43f6d82d8cfbeea1241b)': dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.2003.24(chokidar@4.0.3) @@ -18859,7 +18868,7 @@ snapshots: '@angular/platform-browser': 20.3.21(@angular/common@20.3.21(@angular/core@20.3.21(@angular/compiler@20.3.21)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.21(@angular/compiler@20.3.21)(rxjs@7.8.2)(zone.js@0.15.1)) '@angular/platform-server': 20.3.21(@angular/common@20.3.21(@angular/core@20.3.21(@angular/compiler@20.3.21)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@20.3.21)(@angular/core@20.3.21(@angular/compiler@20.3.21)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.21(@angular/common@20.3.21(@angular/core@20.3.21(@angular/compiler@20.3.21)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.21(@angular/compiler@20.3.21)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) esbuild: 0.28.0 - jest: 30.2.0(@types/node@25.7.0)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)) + jest: 30.2.0(@types/node@25.7.0)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.7.0)(typescript@5.8.3)) jest-environment-jsdom: 29.7.0 karma: 6.4.4 ng-packagr: 20.3.2(@angular/compiler-cli@20.3.21(@angular/compiler@20.3.21)(typescript@5.8.3))(tslib@2.8.1)(typescript@5.8.3) @@ -23308,6 +23317,45 @@ snapshots: - ts-node optional: true + '@jest/core@30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.8.3))': + dependencies: + '@jest/console': 30.2.0 + '@jest/pattern': 30.0.1 + '@jest/reporters': 30.2.0(node-notifier@9.0.1) + '@jest/test-result': 30.2.0 + '@jest/transform': 30.2.0 + '@jest/types': 30.2.0 + '@types/node': 20.19.37 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 4.4.0 + exit-x: 0.2.2 + graceful-fs: 4.2.11 + jest-changed-files: 30.2.0 + jest-config: 30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.8.3)) + jest-haste-map: 30.2.0 + jest-message-util: 30.2.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.2.0 + jest-resolve-dependencies: 30.2.0 + jest-runner: 30.2.0 + jest-runtime: 30.2.0 + jest-snapshot: 30.2.0 + jest-util: 30.2.0 + jest-validate: 30.2.0 + jest-watcher: 30.2.0 + micromatch: 4.0.8 + pretty-format: 30.2.0 + slash: 3.0.0 + optionalDependencies: + node-notifier: 9.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - esbuild-register + - supports-color + - ts-node + optional: true + '@jest/core@30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3))': dependencies: '@jest/console': 30.2.0 @@ -23347,6 +23395,123 @@ snapshots: - ts-node optional: true + '@jest/core@30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.19.37)(typescript@5.8.3))': + dependencies: + '@jest/console': 30.2.0 + '@jest/pattern': 30.0.1 + '@jest/reporters': 30.2.0(node-notifier@9.0.1) + '@jest/test-result': 30.2.0 + '@jest/transform': 30.2.0 + '@jest/types': 30.2.0 + '@types/node': 20.19.37 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 4.4.0 + exit-x: 0.2.2 + graceful-fs: 4.2.11 + jest-changed-files: 30.2.0 + jest-config: 30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.19.37)(typescript@5.8.3)) + jest-haste-map: 30.2.0 + jest-message-util: 30.2.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.2.0 + jest-resolve-dependencies: 30.2.0 + jest-runner: 30.2.0 + jest-runtime: 30.2.0 + jest-snapshot: 30.2.0 + jest-util: 30.2.0 + jest-validate: 30.2.0 + jest-watcher: 30.2.0 + micromatch: 4.0.8 + pretty-format: 30.2.0 + slash: 3.0.0 + optionalDependencies: + node-notifier: 9.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - esbuild-register + - supports-color + - ts-node + optional: true + + '@jest/core@30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.7.0)(typescript@5.8.3))': + dependencies: + '@jest/console': 30.2.0 + '@jest/pattern': 30.0.1 + '@jest/reporters': 30.2.0(node-notifier@9.0.1) + '@jest/test-result': 30.2.0 + '@jest/transform': 30.2.0 + '@jest/types': 30.2.0 + '@types/node': 20.19.37 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 4.4.0 + exit-x: 0.2.2 + graceful-fs: 4.2.11 + jest-changed-files: 30.2.0 + jest-config: 30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.7.0)(typescript@5.8.3)) + jest-haste-map: 30.2.0 + jest-message-util: 30.2.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.2.0 + jest-resolve-dependencies: 30.2.0 + jest-runner: 30.2.0 + jest-runtime: 30.2.0 + jest-snapshot: 30.2.0 + jest-util: 30.2.0 + jest-validate: 30.2.0 + jest-watcher: 30.2.0 + micromatch: 4.0.8 + pretty-format: 30.2.0 + slash: 3.0.0 + optionalDependencies: + node-notifier: 9.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - esbuild-register + - supports-color + - ts-node + optional: true + + '@jest/core@30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.7.0)(typescript@5.9.3))': + dependencies: + '@jest/console': 30.2.0 + '@jest/pattern': 30.0.1 + '@jest/reporters': 30.2.0(node-notifier@9.0.1) + '@jest/test-result': 30.2.0 + '@jest/transform': 30.2.0 + '@jest/types': 30.2.0 + '@types/node': 20.19.37 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 4.4.0 + exit-x: 0.2.2 + graceful-fs: 4.2.11 + jest-changed-files: 30.2.0 + jest-config: 30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.7.0)(typescript@5.9.3)) + jest-haste-map: 30.2.0 + jest-message-util: 30.2.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.2.0 + jest-resolve-dependencies: 30.2.0 + jest-runner: 30.2.0 + jest-runtime: 30.2.0 + jest-snapshot: 30.2.0 + jest-util: 30.2.0 + jest-validate: 30.2.0 + jest-watcher: 30.2.0 + micromatch: 4.0.8 + pretty-format: 30.2.0 + slash: 3.0.0 + optionalDependencies: + node-notifier: 9.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - esbuild-register + - supports-color + - ts-node + optional: true + '@jest/diff-sequences@30.0.1': {} '@jest/diff-sequences@30.3.0': {} @@ -30726,14 +30891,14 @@ snapshots: eslint-plugin-rulesdir: 0.2.2 eslint-plugin-spellcheck: 0.0.20(eslint@9.39.4(jiti@2.6.1)) - eslint-config-devextreme@1.1.10(055279970a77087ef4c7b0df98eac783): + eslint-config-devextreme@1.1.10(035a27461ca62d66425295e10c387bbc): dependencies: '@stylistic/eslint-plugin': 5.10.0(eslint@9.39.4(jiti@2.6.1)) - '@typescript-eslint/eslint-plugin': 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/parser': 8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) + '@typescript-eslint/parser': 8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) eslint: 9.39.4(jiti@2.6.1) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-jest: 29.15.0(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(jest@29.7.0(@types/node@20.12.8)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)))(typescript@5.9.3) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-jest: 29.15.0(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(jest@30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.19.37)(typescript@5.8.3)))(typescript@5.8.3) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-no-only-tests: 3.3.0 eslint-plugin-qunit: 8.2.5(eslint@9.39.4(jiti@2.6.1)) @@ -30741,18 +30906,18 @@ snapshots: eslint-plugin-react-perf: 3.3.3(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-rulesdir: 0.2.2 eslint-plugin-spellcheck: 0.0.20(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-vue: 10.4.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.0.0(eslint@9.39.4(jiti@2.6.1))) - stylelint: 16.22.0(typescript@5.9.3) - stylelint-config-standard: 38.0.0(stylelint@16.22.0(typescript@5.9.3)) + eslint-plugin-vue: 10.4.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.0.0(eslint@9.39.4(jiti@2.6.1))) + stylelint: 16.22.0(typescript@5.8.3) + stylelint-config-standard: 38.0.0(stylelint@16.22.0(typescript@5.8.3)) - eslint-config-devextreme@1.1.10(126991c980e575e73989a19e163db52c): + eslint-config-devextreme@1.1.10(055279970a77087ef4c7b0df98eac783): dependencies: '@stylistic/eslint-plugin': 5.10.0(eslint@9.39.4(jiti@2.6.1)) '@typescript-eslint/eslint-plugin': 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/parser': 8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.4(jiti@2.6.1) eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-jest: 29.15.0(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(jest@30.2.0(@types/node@25.7.0)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)))(typescript@5.9.3) + eslint-plugin-jest: 29.15.0(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(jest@29.7.0(@types/node@20.12.8)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)))(typescript@5.9.3) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-no-only-tests: 3.3.0 eslint-plugin-qunit: 8.2.5(eslint@9.39.4(jiti@2.6.1)) @@ -30783,14 +30948,14 @@ snapshots: stylelint: 16.22.0(typescript@5.9.3) stylelint-config-standard: 38.0.0(stylelint@16.22.0(typescript@5.9.3)) - eslint-config-devextreme@1.1.10(a08ba21ffb24659da3c6462285dc5fef): + eslint-config-devextreme@1.1.10(27ed2754bbdc28fa18814d18b0630f40): dependencies: '@stylistic/eslint-plugin': 5.10.0(eslint@9.39.4(jiti@2.6.1)) - '@typescript-eslint/eslint-plugin': 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5))(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5) - '@typescript-eslint/parser': 8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5) + '@typescript-eslint/eslint-plugin': 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) + '@typescript-eslint/parser': 8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) eslint: 9.39.4(jiti@2.6.1) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5))(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-jest: 29.15.0(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5))(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5))(eslint@9.39.4(jiti@2.6.1))(jest@29.7.0(@types/node@20.12.8)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)))(typescript@4.9.5) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-jest: 29.15.0(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(jest@30.2.0(@types/node@20.12.8)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.8.3)))(typescript@5.8.3) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-no-only-tests: 3.3.0 eslint-plugin-qunit: 8.2.5(eslint@9.39.4(jiti@2.6.1)) @@ -30798,18 +30963,18 @@ snapshots: eslint-plugin-react-perf: 3.3.3(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-rulesdir: 0.2.2 eslint-plugin-spellcheck: 0.0.20(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-vue: 10.4.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.0.0(eslint@9.39.4(jiti@2.6.1))) - stylelint: 16.22.0(typescript@4.9.5) - stylelint-config-standard: 38.0.0(stylelint@16.22.0(typescript@4.9.5)) + eslint-plugin-vue: 10.4.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.0.0(eslint@9.39.4(jiti@2.6.1))) + stylelint: 16.22.0(typescript@5.8.3) + stylelint-config-standard: 38.0.0(stylelint@16.22.0(typescript@5.8.3)) - eslint-config-devextreme@1.1.10(d7e41e94c220a7b440dbf5936b1c1fb4): + eslint-config-devextreme@1.1.10(a08ba21ffb24659da3c6462285dc5fef): dependencies: '@stylistic/eslint-plugin': 5.10.0(eslint@9.39.4(jiti@2.6.1)) '@typescript-eslint/eslint-plugin': 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5))(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5) '@typescript-eslint/parser': 8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5) eslint: 9.39.4(jiti@2.6.1) eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5))(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-jest: 29.15.0(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5))(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5))(eslint@9.39.4(jiti@2.6.1))(jest@29.7.0(@types/node@25.7.0)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)))(typescript@4.9.5) + eslint-plugin-jest: 29.15.0(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5))(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5))(eslint@9.39.4(jiti@2.6.1))(jest@29.7.0(@types/node@20.12.8)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)))(typescript@4.9.5) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-no-only-tests: 3.3.0 eslint-plugin-qunit: 8.2.5(eslint@9.39.4(jiti@2.6.1)) @@ -30821,14 +30986,14 @@ snapshots: stylelint: 16.22.0(typescript@4.9.5) stylelint-config-standard: 38.0.0(stylelint@16.22.0(typescript@4.9.5)) - eslint-config-devextreme@1.1.10(dbcb07078c32d86c4fce4ac459d904a8): + eslint-config-devextreme@1.1.10(ae8e86e33bb8f35b25c20474e34e4557): dependencies: '@stylistic/eslint-plugin': 5.10.0(eslint@9.39.4(jiti@2.6.1)) - '@typescript-eslint/eslint-plugin': 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5))(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5) - '@typescript-eslint/parser': 8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5) + '@typescript-eslint/eslint-plugin': 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.4(jiti@2.6.1) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5))(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-jest: 29.15.0(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5))(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5))(eslint@9.39.4(jiti@2.6.1))(jest@29.7.0(@types/node@20.11.17)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.11.17)(typescript@4.9.5)))(typescript@4.9.5) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-jest: 29.15.0(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(jest@30.2.0(@types/node@25.7.0)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.7.0)(typescript@5.9.3)))(typescript@5.9.3) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-no-only-tests: 3.3.0 eslint-plugin-qunit: 8.2.5(eslint@9.39.4(jiti@2.6.1)) @@ -30836,18 +31001,18 @@ snapshots: eslint-plugin-react-perf: 3.3.3(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-rulesdir: 0.2.2 eslint-plugin-spellcheck: 0.0.20(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-vue: 10.4.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.0.0(eslint@9.39.4(jiti@2.6.1))) - stylelint: 16.22.0(typescript@4.9.5) - stylelint-config-standard: 38.0.0(stylelint@16.22.0(typescript@4.9.5)) + eslint-plugin-vue: 10.4.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.0.0(eslint@9.39.4(jiti@2.6.1))) + stylelint: 16.22.0(typescript@5.9.3) + stylelint-config-standard: 38.0.0(stylelint@16.22.0(typescript@5.9.3)) - eslint-config-devextreme@1.1.10(e6a242bbff638c5d1de6ac61f88210f5): + eslint-config-devextreme@1.1.10(d7e41e94c220a7b440dbf5936b1c1fb4): dependencies: '@stylistic/eslint-plugin': 5.10.0(eslint@9.39.4(jiti@2.6.1)) - '@typescript-eslint/eslint-plugin': 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) - '@typescript-eslint/parser': 8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) + '@typescript-eslint/eslint-plugin': 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5))(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5) + '@typescript-eslint/parser': 8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5) eslint: 9.39.4(jiti@2.6.1) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-jest: 29.15.0(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(jest@30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)))(typescript@5.8.3) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5))(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-jest: 29.15.0(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5))(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5))(eslint@9.39.4(jiti@2.6.1))(jest@29.7.0(@types/node@25.7.0)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)))(typescript@4.9.5) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-no-only-tests: 3.3.0 eslint-plugin-qunit: 8.2.5(eslint@9.39.4(jiti@2.6.1)) @@ -30855,18 +31020,18 @@ snapshots: eslint-plugin-react-perf: 3.3.3(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-rulesdir: 0.2.2 eslint-plugin-spellcheck: 0.0.20(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-vue: 10.4.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.0.0(eslint@9.39.4(jiti@2.6.1))) - stylelint: 16.22.0(typescript@5.8.3) - stylelint-config-standard: 38.0.0(stylelint@16.22.0(typescript@5.8.3)) + eslint-plugin-vue: 10.4.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.0.0(eslint@9.39.4(jiti@2.6.1))) + stylelint: 16.22.0(typescript@4.9.5) + stylelint-config-standard: 38.0.0(stylelint@16.22.0(typescript@4.9.5)) - eslint-config-devextreme@1.1.10(e8375d2d50fc1f6049e8cbb402fc51f0): + eslint-config-devextreme@1.1.10(dbcb07078c32d86c4fce4ac459d904a8): dependencies: '@stylistic/eslint-plugin': 5.10.0(eslint@9.39.4(jiti@2.6.1)) - '@typescript-eslint/eslint-plugin': 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) - '@typescript-eslint/parser': 8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) + '@typescript-eslint/eslint-plugin': 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5))(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5) + '@typescript-eslint/parser': 8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5) eslint: 9.39.4(jiti@2.6.1) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-jest: 29.15.0(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(jest@30.2.0(@types/node@20.12.8)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)))(typescript@5.8.3) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5))(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-jest: 29.15.0(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5))(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5))(eslint@9.39.4(jiti@2.6.1))(jest@29.7.0(@types/node@20.11.17)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.11.17)(typescript@4.9.5)))(typescript@4.9.5) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-no-only-tests: 3.3.0 eslint-plugin-qunit: 8.2.5(eslint@9.39.4(jiti@2.6.1)) @@ -30874,9 +31039,9 @@ snapshots: eslint-plugin-react-perf: 3.3.3(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-rulesdir: 0.2.2 eslint-plugin-spellcheck: 0.0.20(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-vue: 10.4.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.0.0(eslint@9.39.4(jiti@2.6.1))) - stylelint: 16.22.0(typescript@5.8.3) - stylelint-config-standard: 38.0.0(stylelint@16.22.0(typescript@5.8.3)) + eslint-plugin-vue: 10.4.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@4.9.5))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.0.0(eslint@9.39.4(jiti@2.6.1))) + stylelint: 16.22.0(typescript@4.9.5) + stylelint-config-standard: 38.0.0(stylelint@16.22.0(typescript@4.9.5)) eslint-import-resolver-node@0.3.10: dependencies: @@ -31059,24 +31224,24 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-jest@29.15.0(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(jest@30.2.0(@types/node@20.12.8)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)))(typescript@5.8.3): + eslint-plugin-jest@29.15.0(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(jest@30.2.0(@types/node@20.12.8)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.8.3)))(typescript@5.8.3): dependencies: '@typescript-eslint/utils': 8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) eslint: 9.39.4(jiti@2.6.1) optionalDependencies: '@typescript-eslint/eslint-plugin': 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) - jest: 30.2.0(@types/node@20.12.8)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)) + jest: 30.2.0(@types/node@20.12.8)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.8.3)) typescript: 5.8.3 transitivePeerDependencies: - supports-color - eslint-plugin-jest@29.15.0(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(jest@30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)))(typescript@5.8.3): + eslint-plugin-jest@29.15.0(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(jest@30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.19.37)(typescript@5.8.3)))(typescript@5.8.3): dependencies: '@typescript-eslint/utils': 8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) eslint: 9.39.4(jiti@2.6.1) optionalDependencies: '@typescript-eslint/eslint-plugin': 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) - jest: 30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)) + jest: 30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.19.37)(typescript@5.8.3)) typescript: 5.8.3 transitivePeerDependencies: - supports-color @@ -31103,13 +31268,13 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-jest@29.15.0(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(jest@30.2.0(@types/node@25.7.0)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)))(typescript@5.9.3): + eslint-plugin-jest@29.15.0(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(jest@30.2.0(@types/node@25.7.0)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.7.0)(typescript@5.9.3)))(typescript@5.9.3): dependencies: '@typescript-eslint/utils': 8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.4(jiti@2.6.1) optionalDependencies: '@typescript-eslint/eslint-plugin': 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - jest: 30.2.0(@types/node@25.7.0)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)) + jest: 30.2.0(@types/node@25.7.0)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.7.0)(typescript@5.9.3)) typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -33837,6 +34002,28 @@ snapshots: - ts-node optional: true + jest-cli@30.2.0(@types/node@20.12.8)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.8.3)): + dependencies: + '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.8.3)) + '@jest/test-result': 30.2.0 + '@jest/types': 30.2.0 + chalk: 4.1.2 + exit-x: 0.2.2 + import-local: 3.2.0 + jest-config: 30.2.0(@types/node@20.12.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.8.3)) + jest-util: 30.2.0 + jest-validate: 30.2.0 + yargs: 17.7.2 + optionalDependencies: + node-notifier: 9.0.1 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - esbuild-register + - supports-color + - ts-node + optional: true + jest-cli@30.2.0(@types/node@20.12.8)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)): dependencies: '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)) @@ -33859,15 +34046,15 @@ snapshots: - ts-node optional: true - jest-cli@30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)): + jest-cli@30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.19.37)(typescript@5.8.3)): dependencies: - '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)) + '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.19.37)(typescript@5.8.3)) '@jest/test-result': 30.2.0 '@jest/types': 30.2.0 chalk: 4.1.2 exit-x: 0.2.2 import-local: 3.2.0 - jest-config: 30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)) + jest-config: 30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.19.37)(typescript@5.8.3)) jest-util: 30.2.0 jest-validate: 30.2.0 yargs: 17.7.2 @@ -33881,15 +34068,37 @@ snapshots: - ts-node optional: true - jest-cli@30.2.0(@types/node@25.7.0)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)): + jest-cli@30.2.0(@types/node@25.7.0)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.7.0)(typescript@5.8.3)): dependencies: - '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)) + '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.7.0)(typescript@5.8.3)) + '@jest/test-result': 30.2.0 + '@jest/types': 30.2.0 + chalk: 4.1.2 + exit-x: 0.2.2 + import-local: 3.2.0 + jest-config: 30.2.0(@types/node@25.7.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.7.0)(typescript@5.8.3)) + jest-util: 30.2.0 + jest-validate: 30.2.0 + yargs: 17.7.2 + optionalDependencies: + node-notifier: 9.0.1 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - esbuild-register + - supports-color + - ts-node + optional: true + + jest-cli@30.2.0(@types/node@25.7.0)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.7.0)(typescript@5.9.3)): + dependencies: + '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.7.0)(typescript@5.9.3)) '@jest/test-result': 30.2.0 '@jest/types': 30.2.0 chalk: 4.1.2 exit-x: 0.2.2 import-local: 3.2.0 - jest-config: 30.2.0(@types/node@25.7.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)) + jest-config: 30.2.0(@types/node@25.7.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.7.0)(typescript@5.9.3)) jest-util: 30.2.0 jest-validate: 30.2.0 yargs: 17.7.2 @@ -34154,6 +34363,40 @@ snapshots: - supports-color optional: true + jest-config@30.2.0(@types/node@20.12.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.8.3)): + dependencies: + '@babel/core': 7.29.7 + '@jest/get-type': 30.1.0 + '@jest/pattern': 30.0.1 + '@jest/test-sequencer': 30.2.0 + '@jest/types': 30.2.0 + babel-jest: 30.2.0(@babel/core@7.29.7) + chalk: 4.1.2 + ci-info: 4.4.0 + deepmerge: 4.3.1 + glob: 10.5.0 + graceful-fs: 4.2.11 + jest-circus: 30.2.0(babel-plugin-macros@3.1.0) + jest-docblock: 30.2.0 + jest-environment-node: 30.2.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.2.0 + jest-runner: 30.2.0 + jest-util: 30.2.0 + jest-validate: 30.2.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 30.2.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 20.12.8 + ts-node: 10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.8.3) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + optional: true + jest-config@30.2.0(@types/node@20.12.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)): dependencies: '@babel/core': 7.29.7 @@ -34222,6 +34465,40 @@ snapshots: - supports-color optional: true + jest-config@30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.8.3)): + dependencies: + '@babel/core': 7.29.7 + '@jest/get-type': 30.1.0 + '@jest/pattern': 30.0.1 + '@jest/test-sequencer': 30.2.0 + '@jest/types': 30.2.0 + babel-jest: 30.2.0(@babel/core@7.29.7) + chalk: 4.1.2 + ci-info: 4.4.0 + deepmerge: 4.3.1 + glob: 10.5.0 + graceful-fs: 4.2.11 + jest-circus: 30.2.0(babel-plugin-macros@3.1.0) + jest-docblock: 30.2.0 + jest-environment-node: 30.2.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.2.0 + jest-runner: 30.2.0 + jest-util: 30.2.0 + jest-validate: 30.2.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 30.2.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 20.19.37 + ts-node: 10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.8.3) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + optional: true + jest-config@30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)): dependencies: '@babel/core': 7.29.7 @@ -34256,7 +34533,109 @@ snapshots: - supports-color optional: true - jest-config@30.2.0(@types/node@25.7.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)): + jest-config@30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.19.37)(typescript@5.8.3)): + dependencies: + '@babel/core': 7.29.7 + '@jest/get-type': 30.1.0 + '@jest/pattern': 30.0.1 + '@jest/test-sequencer': 30.2.0 + '@jest/types': 30.2.0 + babel-jest: 30.2.0(@babel/core@7.29.7) + chalk: 4.1.2 + ci-info: 4.4.0 + deepmerge: 4.3.1 + glob: 10.5.0 + graceful-fs: 4.2.11 + jest-circus: 30.2.0(babel-plugin-macros@3.1.0) + jest-docblock: 30.2.0 + jest-environment-node: 30.2.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.2.0 + jest-runner: 30.2.0 + jest-util: 30.2.0 + jest-validate: 30.2.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 30.2.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 20.19.37 + ts-node: 10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.19.37)(typescript@5.8.3) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + optional: true + + jest-config@30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.7.0)(typescript@5.8.3)): + dependencies: + '@babel/core': 7.29.7 + '@jest/get-type': 30.1.0 + '@jest/pattern': 30.0.1 + '@jest/test-sequencer': 30.2.0 + '@jest/types': 30.2.0 + babel-jest: 30.2.0(@babel/core@7.29.7) + chalk: 4.1.2 + ci-info: 4.4.0 + deepmerge: 4.3.1 + glob: 10.5.0 + graceful-fs: 4.2.11 + jest-circus: 30.2.0(babel-plugin-macros@3.1.0) + jest-docblock: 30.2.0 + jest-environment-node: 30.2.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.2.0 + jest-runner: 30.2.0 + jest-util: 30.2.0 + jest-validate: 30.2.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 30.2.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 20.19.37 + ts-node: 10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.7.0)(typescript@5.8.3) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + optional: true + + jest-config@30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.7.0)(typescript@5.9.3)): + dependencies: + '@babel/core': 7.29.7 + '@jest/get-type': 30.1.0 + '@jest/pattern': 30.0.1 + '@jest/test-sequencer': 30.2.0 + '@jest/types': 30.2.0 + babel-jest: 30.2.0(@babel/core@7.29.7) + chalk: 4.1.2 + ci-info: 4.4.0 + deepmerge: 4.3.1 + glob: 10.5.0 + graceful-fs: 4.2.11 + jest-circus: 30.2.0(babel-plugin-macros@3.1.0) + jest-docblock: 30.2.0 + jest-environment-node: 30.2.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.2.0 + jest-runner: 30.2.0 + jest-util: 30.2.0 + jest-validate: 30.2.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 30.2.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 20.19.37 + ts-node: 10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.7.0)(typescript@5.9.3) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + optional: true + + jest-config@30.2.0(@types/node@25.7.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.7.0)(typescript@5.8.3)): dependencies: '@babel/core': 7.29.7 '@jest/get-type': 30.1.0 @@ -34284,7 +34663,41 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 25.7.0 - ts-node: 10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3) + ts-node: 10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.7.0)(typescript@5.8.3) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + optional: true + + jest-config@30.2.0(@types/node@25.7.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.7.0)(typescript@5.9.3)): + dependencies: + '@babel/core': 7.29.7 + '@jest/get-type': 30.1.0 + '@jest/pattern': 30.0.1 + '@jest/test-sequencer': 30.2.0 + '@jest/types': 30.2.0 + babel-jest: 30.2.0(@babel/core@7.29.7) + chalk: 4.1.2 + ci-info: 4.4.0 + deepmerge: 4.3.1 + glob: 10.5.0 + graceful-fs: 4.2.11 + jest-circus: 30.2.0(babel-plugin-macros@3.1.0) + jest-docblock: 30.2.0 + jest-environment-node: 30.2.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.2.0 + jest-runner: 30.2.0 + jest-util: 30.2.0 + jest-validate: 30.2.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 30.2.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 25.7.0 + ts-node: 10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.7.0)(typescript@5.9.3) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -35065,6 +35478,22 @@ snapshots: - ts-node optional: true + jest@30.2.0(@types/node@20.12.8)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.8.3)): + dependencies: + '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.8.3)) + '@jest/types': 30.2.0 + import-local: 3.2.0 + jest-cli: 30.2.0(@types/node@20.12.8)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.8.3)) + optionalDependencies: + node-notifier: 9.0.1 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - esbuild-register + - supports-color + - ts-node + optional: true + jest@30.2.0(@types/node@20.12.8)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)): dependencies: '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)) @@ -35081,12 +35510,12 @@ snapshots: - ts-node optional: true - jest@30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)): + jest@30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.19.37)(typescript@5.8.3)): dependencies: - '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)) + '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.19.37)(typescript@5.8.3)) '@jest/types': 30.2.0 import-local: 3.2.0 - jest-cli: 30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)) + jest-cli: 30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.19.37)(typescript@5.8.3)) optionalDependencies: node-notifier: 9.0.1 transitivePeerDependencies: @@ -35097,12 +35526,28 @@ snapshots: - ts-node optional: true - jest@30.2.0(@types/node@25.7.0)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)): + jest@30.2.0(@types/node@25.7.0)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.7.0)(typescript@5.8.3)): dependencies: - '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)) + '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.7.0)(typescript@5.8.3)) + '@jest/types': 30.2.0 + import-local: 3.2.0 + jest-cli: 30.2.0(@types/node@25.7.0)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.7.0)(typescript@5.8.3)) + optionalDependencies: + node-notifier: 9.0.1 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - esbuild-register + - supports-color + - ts-node + optional: true + + jest@30.2.0(@types/node@25.7.0)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.7.0)(typescript@5.9.3)): + dependencies: + '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.7.0)(typescript@5.9.3)) '@jest/types': 30.2.0 import-local: 3.2.0 - jest-cli: 30.2.0(@types/node@25.7.0)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)) + jest-cli: 30.2.0(@types/node@25.7.0)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.7.0)(typescript@5.9.3)) optionalDependencies: node-notifier: 9.0.1 transitivePeerDependencies: @@ -39915,7 +40360,7 @@ snapshots: jest-worker: 27.5.1 schema-utils: 4.3.3 terser: 5.46.1 - webpack: 5.105.4(@swc/core@1.15.30(@swc/helpers@0.5.21))(webpack-cli@5.1.4) + webpack: 5.105.4(@swc/core@1.15.30(@swc/helpers@0.5.21))(webpack-cli@4.10.0) optionalDependencies: '@swc/core': 1.15.30(@swc/helpers@0.5.21) @@ -40550,6 +40995,27 @@ snapshots: optionalDependencies: '@swc/core': 1.15.30(@swc/helpers@0.5.21) + ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.8.3): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.12 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 20.12.8 + acorn: 8.16.0 + acorn-walk: 8.3.5 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.4 + make-error: 1.3.6 + typescript: 5.8.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + optionalDependencies: + '@swc/core': 1.15.30(@swc/helpers@0.5.21) + optional: true + ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3): dependencies: '@cspotcode/source-map-support': 0.8.1 @@ -40570,6 +41036,69 @@ snapshots: optionalDependencies: '@swc/core': 1.15.30(@swc/helpers@0.5.21) + ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.19.37)(typescript@5.8.3): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.12 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 20.19.37 + acorn: 8.16.0 + acorn-walk: 8.3.5 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.4 + make-error: 1.3.6 + typescript: 5.8.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + optionalDependencies: + '@swc/core': 1.15.30(@swc/helpers@0.5.21) + optional: true + + ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.7.0)(typescript@5.8.3): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.12 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 25.7.0 + acorn: 8.16.0 + acorn-walk: 8.3.5 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.4 + make-error: 1.3.6 + typescript: 5.8.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + optionalDependencies: + '@swc/core': 1.15.30(@swc/helpers@0.5.21) + optional: true + + ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.7.0)(typescript@5.9.3): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.12 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 25.7.0 + acorn: 8.16.0 + acorn-walk: 8.3.5 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.4 + make-error: 1.3.6 + typescript: 5.9.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + optionalDependencies: + '@swc/core': 1.15.30(@swc/helpers@0.5.21) + optional: true + tsc-alias@1.8.16: dependencies: chokidar: 3.6.0 From 69d44c530b19c1cda89c7f8c10a872778fee6d1b Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Thu, 18 Jun 2026 17:21:00 +0200 Subject: [PATCH 17/23] use --workspace-root --- packages/devextreme-scss/package.json | 4 ++-- packages/devextreme/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/devextreme-scss/package.json b/packages/devextreme-scss/package.json index a380c72d244d..9f77d9a1e81a 100644 --- a/packages/devextreme-scss/package.json +++ b/packages/devextreme-scss/package.json @@ -11,10 +11,10 @@ "ts-jest": "29.1.2" }, "scripts": { - "build": "pnpm nx build devextreme-scss", + "build": "pnpm --workspace-root nx build devextreme-scss", "lint": "stylelint scss/widgets", "test": "jest --no-coverage --runInBand --config=./tests/jest.config.json", - "watch": "pnpm nx run devextreme-scss --target=watch" + "watch": "pnpm --workspace-root nx run devextreme-scss --target=watch" }, "version": "26.1.3" } diff --git a/packages/devextreme/package.json b/packages/devextreme/package.json index 62f8ec880ceb..e673274f1b1c 100644 --- a/packages/devextreme/package.json +++ b/packages/devextreme/package.json @@ -224,7 +224,7 @@ "build:testcafe": "cross-env DEVEXTREME_TEST_CI=TRUE BUILD_ESM_PACKAGE=true BUILD_TESTCAFE=TRUE gulp default", "build-npm-devextreme": "cross-env BUILD_ESM_PACKAGE=true gulp default", "build-dist": "cross-env BUILD_ESM_PACKAGE=true gulp default --uglify", - "build-themes": "pnpm nx build devextreme-scss", + "build-themes": "pnpm --workspace-root nx build devextreme-scss", "build:react": "gulp generate-react", "build:react:watch": "gulp generate-react-watch", "build:react:typescript": "gulp generate-react-typescript", From 249bd0c0c4a4722acadc6685255da4c828c29f0c Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Thu, 18 Jun 2026 22:52:01 +0200 Subject: [PATCH 18/23] refactor: use projectRequire --- packages/devextreme-scss/package.json | 3 + .../executors/scss-build/scss-build.impl.ts | 13 +- pnpm-lock.yaml | 703 +++++++++++++++--- 3 files changed, 625 insertions(+), 94 deletions(-) diff --git a/packages/devextreme-scss/package.json b/packages/devextreme-scss/package.json index 9f77d9a1e81a..4c10648aafdd 100644 --- a/packages/devextreme-scss/package.json +++ b/packages/devextreme-scss/package.json @@ -2,7 +2,10 @@ "name": "devextreme-scss", "type": "module", "devDependencies": { + "autoprefixer": "10.5.0", + "chokidar": "5.0.0", "clean-css": "5.3.3", + "postcss": "8.5.10", "sass-embedded": "1.93.3", "@stylistic/stylelint-plugin": "3.1.3", "stylelint": "catalog:", diff --git a/packages/nx-infra-plugin/src/executors/scss-build/scss-build.impl.ts b/packages/nx-infra-plugin/src/executors/scss-build/scss-build.impl.ts index 1c30231d5128..1b17e2b00cb4 100644 --- a/packages/nx-infra-plugin/src/executors/scss-build/scss-build.impl.ts +++ b/packages/nx-infra-plugin/src/executors/scss-build/scss-build.impl.ts @@ -120,24 +120,23 @@ async function generateScssBundles( function loadDependencies(projectRoot: string): BuildDependencies { const projectRequire = createRequire(path.join(projectRoot, 'package.json')); - const workspaceRequire = createRequire(path.join(projectRoot, '..', '..', 'package.json')); return { sass: projectRequire('sass-embedded'), - postcss: workspaceRequire('postcss'), - autoprefixer: workspaceRequire('autoprefixer'), - chokidar: workspaceRequire('chokidar'), - CleanCss: workspaceRequire('clean-css'), + postcss: projectRequire('postcss'), + autoprefixer: projectRequire('autoprefixer'), + chokidar: projectRequire('chokidar'), + CleanCss: projectRequire('clean-css'), themeOptions: projectRequire(path.resolve(projectRoot, 'build/theme-options.cjs')) as { getThemes: () => Array<[string, string, string, string?]>; }, cleanCssSanitizeOptions: projectRequire( path.resolve(projectRoot, 'build/clean-css-options.json'), ), - cleanCssDevOptions: workspaceRequire( + cleanCssDevOptions: projectRequire( path.resolve(projectRoot, '../devextreme-themebuilder/src/data/clean-css-options.json'), ), - devextremeVersion: workspaceRequire(path.resolve(projectRoot, '../devextreme/package.json')) + devextremeVersion: projectRequire(path.resolve(projectRoot, '../devextreme/package.json')) .version, }; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 81a34848040c..2768d1e0c542 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1006,7 +1006,7 @@ importers: version: 9.39.4(jiti@2.6.1) eslint-config-devextreme: specifier: 'catalog:' - version: 1.1.11(f19ad25a54c4777ebfabafe65d911a45) + version: 1.1.11(77dee72e16455f879ff54a28c4ae5760) eslint-plugin-i18n: specifier: 'catalog:' version: 2.4.0 @@ -1078,7 +1078,7 @@ importers: version: 9.39.4(jiti@2.6.1) eslint-config-devextreme: specifier: 'catalog:' - version: 1.1.11(3326f177fdbc23aca8abf2cbc262d15f) + version: 1.1.11(4b96114756c692b5f27fe0b434b0b820) eslint-migration-utils: specifier: workspace:* version: link:../../packages/eslint-migration-utils @@ -1295,10 +1295,10 @@ importers: devDependencies: '@analogjs/vite-plugin-angular': specifier: 1.22.5 - version: 1.22.5(@angular-devkit/build-angular@20.3.24(5c6de5c3c6a4de5495ca9bb245eb0817))(@angular/build@20.3.24(9048a156accbad9f5e06c1d521ff0122)) + version: 1.22.5(@angular-devkit/build-angular@20.3.24(427bd81b59e513cb902108a8cb2be6fd))(@angular/build@20.3.24(9048a156accbad9f5e06c1d521ff0122)) '@angular-devkit/build-angular': specifier: catalog:angular - version: 20.3.24(5c6de5c3c6a4de5495ca9bb245eb0817) + version: 20.3.24(427bd81b59e513cb902108a8cb2be6fd) '@angular/cli': specifier: catalog:angular version: 20.3.24(@types/node@25.9.3)(chokidar@4.0.3) @@ -1919,7 +1919,7 @@ importers: version: 9.39.4(jiti@2.6.1) eslint-config-devextreme: specifier: 'catalog:' - version: 1.1.11(64f1ac9f0cad481ebbdffba01b0af57a) + version: 1.1.11(a0ddd35370eff9a9db851227fa5860e1) eslint-migration-utils: specifier: workspace:* version: link:../eslint-migration-utils @@ -2107,9 +2107,18 @@ importers: '@stylistic/stylelint-plugin': specifier: 3.1.3 version: 3.1.3(stylelint@16.22.0(typescript@5.9.3)) + autoprefixer: + specifier: 10.5.0 + version: 10.5.0(postcss@8.5.10) + chokidar: + specifier: 5.0.0 + version: 5.0.0 clean-css: specifier: 5.3.3 version: 5.3.3 + postcss: + specifier: 8.5.10 + version: 8.5.10 sass-embedded: specifier: 1.93.3 version: 1.93.3 @@ -17966,12 +17975,12 @@ snapshots: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 - '@analogjs/vite-plugin-angular@1.22.5(@angular-devkit/build-angular@20.3.24(5c6de5c3c6a4de5495ca9bb245eb0817))(@angular/build@20.3.24(9048a156accbad9f5e06c1d521ff0122))': + '@analogjs/vite-plugin-angular@1.22.5(@angular-devkit/build-angular@20.3.24(427bd81b59e513cb902108a8cb2be6fd))(@angular/build@20.3.24(9048a156accbad9f5e06c1d521ff0122))': dependencies: ts-morph: 21.0.1 vfile: 6.0.3 optionalDependencies: - '@angular-devkit/build-angular': 20.3.24(5c6de5c3c6a4de5495ca9bb245eb0817) + '@angular-devkit/build-angular': 20.3.24(427bd81b59e513cb902108a8cb2be6fd) '@angular/build': 20.3.24(9048a156accbad9f5e06c1d521ff0122) '@angular-devkit/architect@0.2003.24(chokidar@4.0.3)': @@ -17988,7 +17997,7 @@ snapshots: transitivePeerDependencies: - chokidar - '@angular-devkit/build-angular@20.3.24(5c6de5c3c6a4de5495ca9bb245eb0817)': + '@angular-devkit/build-angular@20.3.24(427bd81b59e513cb902108a8cb2be6fd)': dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.2003.24(chokidar@4.0.3) @@ -18051,7 +18060,7 @@ snapshots: '@angular/platform-browser': 20.3.25(@angular/common@20.3.25(@angular/core@20.3.25(@angular/compiler@20.3.25)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.25(@angular/compiler@20.3.25)(rxjs@7.8.2)(zone.js@0.15.1)) '@angular/platform-server': 20.3.25(@angular/common@20.3.25(@angular/core@20.3.25(@angular/compiler@20.3.25)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@20.3.25)(@angular/core@20.3.25(@angular/compiler@20.3.25)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.25(@angular/common@20.3.25(@angular/core@20.3.25(@angular/compiler@20.3.25)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.25(@angular/compiler@20.3.25)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) esbuild: 0.28.1 - jest: 30.2.0(@types/node@25.9.3)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)) + jest: 30.2.0(@types/node@25.9.3)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.9.3)(typescript@5.8.3)) jest-environment-jsdom: 29.7.0 karma: 6.4.4 ng-packagr: 20.3.2(@angular/compiler-cli@20.3.25(@angular/compiler@20.3.25)(typescript@5.8.3))(tslib@2.8.1)(typescript@5.8.3) @@ -21541,6 +21550,45 @@ snapshots: - ts-node optional: true + '@jest/core@30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.8.3))': + dependencies: + '@jest/console': 30.2.0 + '@jest/pattern': 30.0.1 + '@jest/reporters': 30.2.0(node-notifier@9.0.1) + '@jest/test-result': 30.2.0 + '@jest/transform': 30.2.0 + '@jest/types': 30.2.0 + '@types/node': 20.19.37 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 4.4.0 + exit-x: 0.2.2 + graceful-fs: 4.2.11 + jest-changed-files: 30.2.0 + jest-config: 30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.8.3)) + jest-haste-map: 30.2.0 + jest-message-util: 30.2.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.2.0 + jest-resolve-dependencies: 30.2.0 + jest-runner: 30.2.0 + jest-runtime: 30.2.0 + jest-snapshot: 30.2.0 + jest-util: 30.2.0 + jest-validate: 30.2.0 + jest-watcher: 30.2.0 + micromatch: 4.0.8 + pretty-format: 30.2.0 + slash: 3.0.0 + optionalDependencies: + node-notifier: 9.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - esbuild-register + - supports-color + - ts-node + optional: true + '@jest/core@30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3))': dependencies: '@jest/console': 30.2.0 @@ -21580,6 +21628,123 @@ snapshots: - ts-node optional: true + '@jest/core@30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.19.37)(typescript@5.8.3))': + dependencies: + '@jest/console': 30.2.0 + '@jest/pattern': 30.0.1 + '@jest/reporters': 30.2.0(node-notifier@9.0.1) + '@jest/test-result': 30.2.0 + '@jest/transform': 30.2.0 + '@jest/types': 30.2.0 + '@types/node': 20.19.37 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 4.4.0 + exit-x: 0.2.2 + graceful-fs: 4.2.11 + jest-changed-files: 30.2.0 + jest-config: 30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.19.37)(typescript@5.8.3)) + jest-haste-map: 30.2.0 + jest-message-util: 30.2.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.2.0 + jest-resolve-dependencies: 30.2.0 + jest-runner: 30.2.0 + jest-runtime: 30.2.0 + jest-snapshot: 30.2.0 + jest-util: 30.2.0 + jest-validate: 30.2.0 + jest-watcher: 30.2.0 + micromatch: 4.0.8 + pretty-format: 30.2.0 + slash: 3.0.0 + optionalDependencies: + node-notifier: 9.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - esbuild-register + - supports-color + - ts-node + optional: true + + '@jest/core@30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.9.3)(typescript@5.8.3))': + dependencies: + '@jest/console': 30.2.0 + '@jest/pattern': 30.0.1 + '@jest/reporters': 30.2.0(node-notifier@9.0.1) + '@jest/test-result': 30.2.0 + '@jest/transform': 30.2.0 + '@jest/types': 30.2.0 + '@types/node': 20.19.37 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 4.4.0 + exit-x: 0.2.2 + graceful-fs: 4.2.11 + jest-changed-files: 30.2.0 + jest-config: 30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.9.3)(typescript@5.8.3)) + jest-haste-map: 30.2.0 + jest-message-util: 30.2.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.2.0 + jest-resolve-dependencies: 30.2.0 + jest-runner: 30.2.0 + jest-runtime: 30.2.0 + jest-snapshot: 30.2.0 + jest-util: 30.2.0 + jest-validate: 30.2.0 + jest-watcher: 30.2.0 + micromatch: 4.0.8 + pretty-format: 30.2.0 + slash: 3.0.0 + optionalDependencies: + node-notifier: 9.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - esbuild-register + - supports-color + - ts-node + optional: true + + '@jest/core@30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.9.3)(typescript@5.9.3))': + dependencies: + '@jest/console': 30.2.0 + '@jest/pattern': 30.0.1 + '@jest/reporters': 30.2.0(node-notifier@9.0.1) + '@jest/test-result': 30.2.0 + '@jest/transform': 30.2.0 + '@jest/types': 30.2.0 + '@types/node': 20.19.37 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 4.4.0 + exit-x: 0.2.2 + graceful-fs: 4.2.11 + jest-changed-files: 30.2.0 + jest-config: 30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.9.3)(typescript@5.9.3)) + jest-haste-map: 30.2.0 + jest-message-util: 30.2.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.2.0 + jest-resolve-dependencies: 30.2.0 + jest-runner: 30.2.0 + jest-runtime: 30.2.0 + jest-snapshot: 30.2.0 + jest-util: 30.2.0 + jest-validate: 30.2.0 + jest-watcher: 30.2.0 + micromatch: 4.0.8 + pretty-format: 30.2.0 + slash: 3.0.0 + optionalDependencies: + node-notifier: 9.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - esbuild-register + - supports-color + - ts-node + optional: true + '@jest/diff-sequences@30.0.1': {} '@jest/diff-sequences@30.3.0': {} @@ -28669,14 +28834,14 @@ snapshots: stylelint: 16.22.0(typescript@5.9.3) stylelint-config-standard: 38.0.0(stylelint@16.22.0(typescript@5.9.3)) - eslint-config-devextreme@1.1.11(3326f177fdbc23aca8abf2cbc262d15f): + eslint-config-devextreme@1.1.11(4b96114756c692b5f27fe0b434b0b820): dependencies: '@stylistic/eslint-plugin': 5.10.0(eslint@9.39.4(jiti@2.6.1)) '@typescript-eslint/eslint-plugin': 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) '@typescript-eslint/parser': 8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) eslint: 9.39.4(jiti@2.6.1) eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-jest: 29.15.2(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(jest@30.2.0(@types/node@20.12.8)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)))(typescript@5.8.3) + eslint-plugin-jest: 29.15.2(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(jest@30.2.0(@types/node@20.12.8)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.8.3)))(typescript@5.8.3) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-no-only-tests: 3.3.0 eslint-plugin-qunit: 8.2.6(eslint@9.39.4(jiti@2.6.1)) @@ -28707,14 +28872,14 @@ snapshots: stylelint: 16.22.0(typescript@4.9.5) stylelint-config-standard: 38.0.0(stylelint@16.22.0(typescript@4.9.5)) - eslint-config-devextreme@1.1.11(64f1ac9f0cad481ebbdffba01b0af57a): + eslint-config-devextreme@1.1.11(77dee72e16455f879ff54a28c4ae5760): dependencies: '@stylistic/eslint-plugin': 5.10.0(eslint@9.39.4(jiti@2.6.1)) - '@typescript-eslint/eslint-plugin': 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) - '@typescript-eslint/parser': 8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) + '@typescript-eslint/eslint-plugin': 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.4(jiti@2.6.1) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-jest: 29.15.2(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(jest@30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)))(typescript@5.8.3) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-jest: 29.15.2(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(jest@30.2.0(@types/node@25.9.3)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.9.3)(typescript@5.9.3)))(typescript@5.9.3) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-no-only-tests: 3.3.0 eslint-plugin-qunit: 8.2.6(eslint@9.39.4(jiti@2.6.1)) @@ -28722,9 +28887,9 @@ snapshots: eslint-plugin-react-perf: 3.3.3(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-rulesdir: 0.2.2 eslint-plugin-spellcheck: 0.0.20(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-vue: 10.4.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.0.0(eslint@9.39.4(jiti@2.6.1))) - stylelint: 16.22.0(typescript@5.8.3) - stylelint-config-standard: 38.0.0(stylelint@16.22.0(typescript@5.8.3)) + eslint-plugin-vue: 10.4.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.0.0(eslint@9.39.4(jiti@2.6.1))) + stylelint: 16.22.0(typescript@5.9.3) + stylelint-config-standard: 38.0.0(stylelint@16.22.0(typescript@5.9.3)) eslint-config-devextreme@1.1.11(791322f479eadf2c17aee9b712f42d27): dependencies: @@ -28745,6 +28910,25 @@ snapshots: stylelint: 16.22.0(typescript@4.9.5) stylelint-config-standard: 38.0.0(stylelint@16.22.0(typescript@4.9.5)) + eslint-config-devextreme@1.1.11(a0ddd35370eff9a9db851227fa5860e1): + dependencies: + '@stylistic/eslint-plugin': 5.10.0(eslint@9.39.4(jiti@2.6.1)) + '@typescript-eslint/eslint-plugin': 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) + '@typescript-eslint/parser': 8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) + eslint: 9.39.4(jiti@2.6.1) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-jest: 29.15.2(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(jest@30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.19.37)(typescript@5.8.3)))(typescript@5.8.3) + eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-no-only-tests: 3.3.0 + eslint-plugin-qunit: 8.2.6(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-react: 7.37.5(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-react-perf: 3.3.3(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-rulesdir: 0.2.2 + eslint-plugin-spellcheck: 0.0.20(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-vue: 10.4.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.0.0(eslint@9.39.4(jiti@2.6.1))) + stylelint: 16.22.0(typescript@5.8.3) + stylelint-config-standard: 38.0.0(stylelint@16.22.0(typescript@5.8.3)) + eslint-config-devextreme@1.1.11(c34ec7971d22e8cf41745c85af89a33d): dependencies: '@stylistic/eslint-plugin': 5.10.0(eslint@9.39.4(jiti@2.6.1)) @@ -28783,25 +28967,6 @@ snapshots: stylelint: 16.22.0(typescript@4.9.5) stylelint-config-standard: 38.0.0(stylelint@16.22.0(typescript@4.9.5)) - eslint-config-devextreme@1.1.11(f19ad25a54c4777ebfabafe65d911a45): - dependencies: - '@stylistic/eslint-plugin': 5.10.0(eslint@9.39.4(jiti@2.6.1)) - '@typescript-eslint/eslint-plugin': 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/parser': 8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - eslint: 9.39.4(jiti@2.6.1) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-jest: 29.15.2(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(jest@30.2.0(@types/node@25.9.3)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)))(typescript@5.9.3) - eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-no-only-tests: 3.3.0 - eslint-plugin-qunit: 8.2.6(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-react: 7.37.5(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-react-perf: 3.3.3(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-rulesdir: 0.2.2 - eslint-plugin-spellcheck: 0.0.20(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-vue: 10.4.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(vue-eslint-parser@10.0.0(eslint@9.39.4(jiti@2.6.1))) - stylelint: 16.22.0(typescript@5.9.3) - stylelint-config-standard: 38.0.0(stylelint@16.22.0(typescript@5.9.3)) - eslint-import-resolver-node@0.3.10: dependencies: debug: 3.2.7 @@ -28983,24 +29148,24 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-jest@29.15.2(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(jest@30.2.0(@types/node@20.12.8)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)))(typescript@5.8.3): + eslint-plugin-jest@29.15.2(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(jest@30.2.0(@types/node@20.12.8)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.8.3)))(typescript@5.8.3): dependencies: '@typescript-eslint/utils': 8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) eslint: 9.39.4(jiti@2.6.1) optionalDependencies: '@typescript-eslint/eslint-plugin': 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) - jest: 30.2.0(@types/node@20.12.8)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)) + jest: 30.2.0(@types/node@20.12.8)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.8.3)) typescript: 5.8.3 transitivePeerDependencies: - supports-color - eslint-plugin-jest@29.15.2(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(jest@30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)))(typescript@5.8.3): + eslint-plugin-jest@29.15.2(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(jest@30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.19.37)(typescript@5.8.3)))(typescript@5.8.3): dependencies: '@typescript-eslint/utils': 8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) eslint: 9.39.4(jiti@2.6.1) optionalDependencies: '@typescript-eslint/eslint-plugin': 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.8.3) - jest: 30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)) + jest: 30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.19.37)(typescript@5.8.3)) typescript: 5.8.3 transitivePeerDependencies: - supports-color @@ -29027,13 +29192,13 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-jest@29.15.2(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(jest@30.2.0(@types/node@25.9.3)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)))(typescript@5.9.3): + eslint-plugin-jest@29.15.2(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(jest@30.2.0(@types/node@25.9.3)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.9.3)(typescript@5.9.3)))(typescript@5.9.3): dependencies: '@typescript-eslint/utils': 8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.4(jiti@2.6.1) optionalDependencies: '@typescript-eslint/eslint-plugin': 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - jest: 30.2.0(@types/node@25.9.3)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)) + jest: 30.2.0(@types/node@25.9.3)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.9.3)(typescript@5.9.3)) typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -31675,6 +31840,28 @@ snapshots: - ts-node optional: true + jest-cli@30.2.0(@types/node@20.12.8)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.8.3)): + dependencies: + '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.8.3)) + '@jest/test-result': 30.2.0 + '@jest/types': 30.2.0 + chalk: 4.1.2 + exit-x: 0.2.2 + import-local: 3.2.0 + jest-config: 30.2.0(@types/node@20.12.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.8.3)) + jest-util: 30.2.0 + jest-validate: 30.2.0 + yargs: 17.7.2 + optionalDependencies: + node-notifier: 9.0.1 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - esbuild-register + - supports-color + - ts-node + optional: true + jest-cli@30.2.0(@types/node@20.12.8)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)): dependencies: '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)) @@ -31697,15 +31884,15 @@ snapshots: - ts-node optional: true - jest-cli@30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)): + jest-cli@30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.19.37)(typescript@5.8.3)): dependencies: - '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)) + '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.19.37)(typescript@5.8.3)) '@jest/test-result': 30.2.0 '@jest/types': 30.2.0 chalk: 4.1.2 exit-x: 0.2.2 import-local: 3.2.0 - jest-config: 30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)) + jest-config: 30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.19.37)(typescript@5.8.3)) jest-util: 30.2.0 jest-validate: 30.2.0 yargs: 17.7.2 @@ -31719,15 +31906,15 @@ snapshots: - ts-node optional: true - jest-cli@30.2.0(@types/node@25.9.3)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)): + jest-cli@30.2.0(@types/node@25.9.3)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.9.3)(typescript@5.8.3)): dependencies: - '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)) + '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.9.3)(typescript@5.8.3)) '@jest/test-result': 30.2.0 '@jest/types': 30.2.0 chalk: 4.1.2 exit-x: 0.2.2 import-local: 3.2.0 - jest-config: 30.2.0(@types/node@25.9.3)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)) + jest-config: 30.2.0(@types/node@25.9.3)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.9.3)(typescript@5.8.3)) jest-util: 30.2.0 jest-validate: 30.2.0 yargs: 17.7.2 @@ -31741,38 +31928,60 @@ snapshots: - ts-node optional: true - jest-config@29.7.0(@types/node@18.19.130)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@18.19.130)(typescript@4.9.5)): + jest-cli@30.2.0(@types/node@25.9.3)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.9.3)(typescript@5.9.3)): dependencies: - '@babel/core': 7.29.7 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.29.7) + '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.9.3)(typescript@5.9.3)) + '@jest/test-result': 30.2.0 + '@jest/types': 30.2.0 chalk: 4.1.2 - ci-info: 3.9.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.7.0(babel-plugin-macros@3.1.0) - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.8 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 + exit-x: 0.2.2 + import-local: 3.2.0 + jest-config: 30.2.0(@types/node@25.9.3)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.9.3)(typescript@5.9.3)) + jest-util: 30.2.0 + jest-validate: 30.2.0 + yargs: 17.7.2 optionalDependencies: - '@types/node': 18.19.130 - ts-node: 10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@18.19.130)(typescript@4.9.5) + node-notifier: 9.0.1 transitivePeerDependencies: + - '@types/node' - babel-plugin-macros + - esbuild-register - supports-color + - ts-node + optional: true - jest-config@29.7.0(@types/node@20.11.17)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.11.17)(typescript@4.9.5)): + jest-config@29.7.0(@types/node@18.19.130)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@18.19.130)(typescript@4.9.5)): + dependencies: + '@babel/core': 7.29.7 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.29.7) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0(babel-plugin-macros@3.1.0) + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 18.19.130 + ts-node: 10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@18.19.130)(typescript@4.9.5) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-config@29.7.0(@types/node@20.11.17)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.11.17)(typescript@4.9.5)): dependencies: '@babel/core': 7.29.7 '@jest/test-sequencer': 29.7.0 @@ -31992,6 +32201,40 @@ snapshots: - supports-color optional: true + jest-config@30.2.0(@types/node@20.12.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.8.3)): + dependencies: + '@babel/core': 7.29.7 + '@jest/get-type': 30.1.0 + '@jest/pattern': 30.0.1 + '@jest/test-sequencer': 30.2.0 + '@jest/types': 30.2.0 + babel-jest: 30.2.0(@babel/core@7.29.7) + chalk: 4.1.2 + ci-info: 4.4.0 + deepmerge: 4.3.1 + glob: 10.5.0 + graceful-fs: 4.2.11 + jest-circus: 30.2.0(babel-plugin-macros@3.1.0) + jest-docblock: 30.2.0 + jest-environment-node: 30.2.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.2.0 + jest-runner: 30.2.0 + jest-util: 30.2.0 + jest-validate: 30.2.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 30.2.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 20.12.8 + ts-node: 10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.8.3) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + optional: true + jest-config@30.2.0(@types/node@20.12.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)): dependencies: '@babel/core': 7.29.7 @@ -32060,6 +32303,40 @@ snapshots: - supports-color optional: true + jest-config@30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.8.3)): + dependencies: + '@babel/core': 7.29.7 + '@jest/get-type': 30.1.0 + '@jest/pattern': 30.0.1 + '@jest/test-sequencer': 30.2.0 + '@jest/types': 30.2.0 + babel-jest: 30.2.0(@babel/core@7.29.7) + chalk: 4.1.2 + ci-info: 4.4.0 + deepmerge: 4.3.1 + glob: 10.5.0 + graceful-fs: 4.2.11 + jest-circus: 30.2.0(babel-plugin-macros@3.1.0) + jest-docblock: 30.2.0 + jest-environment-node: 30.2.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.2.0 + jest-runner: 30.2.0 + jest-util: 30.2.0 + jest-validate: 30.2.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 30.2.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 20.19.37 + ts-node: 10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.8.3) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + optional: true + jest-config@30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)): dependencies: '@babel/core': 7.29.7 @@ -32094,7 +32371,109 @@ snapshots: - supports-color optional: true - jest-config@30.2.0(@types/node@25.9.3)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)): + jest-config@30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.19.37)(typescript@5.8.3)): + dependencies: + '@babel/core': 7.29.7 + '@jest/get-type': 30.1.0 + '@jest/pattern': 30.0.1 + '@jest/test-sequencer': 30.2.0 + '@jest/types': 30.2.0 + babel-jest: 30.2.0(@babel/core@7.29.7) + chalk: 4.1.2 + ci-info: 4.4.0 + deepmerge: 4.3.1 + glob: 10.5.0 + graceful-fs: 4.2.11 + jest-circus: 30.2.0(babel-plugin-macros@3.1.0) + jest-docblock: 30.2.0 + jest-environment-node: 30.2.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.2.0 + jest-runner: 30.2.0 + jest-util: 30.2.0 + jest-validate: 30.2.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 30.2.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 20.19.37 + ts-node: 10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.19.37)(typescript@5.8.3) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + optional: true + + jest-config@30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.9.3)(typescript@5.8.3)): + dependencies: + '@babel/core': 7.29.7 + '@jest/get-type': 30.1.0 + '@jest/pattern': 30.0.1 + '@jest/test-sequencer': 30.2.0 + '@jest/types': 30.2.0 + babel-jest: 30.2.0(@babel/core@7.29.7) + chalk: 4.1.2 + ci-info: 4.4.0 + deepmerge: 4.3.1 + glob: 10.5.0 + graceful-fs: 4.2.11 + jest-circus: 30.2.0(babel-plugin-macros@3.1.0) + jest-docblock: 30.2.0 + jest-environment-node: 30.2.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.2.0 + jest-runner: 30.2.0 + jest-util: 30.2.0 + jest-validate: 30.2.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 30.2.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 20.19.37 + ts-node: 10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.9.3)(typescript@5.8.3) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + optional: true + + jest-config@30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.9.3)(typescript@5.9.3)): + dependencies: + '@babel/core': 7.29.7 + '@jest/get-type': 30.1.0 + '@jest/pattern': 30.0.1 + '@jest/test-sequencer': 30.2.0 + '@jest/types': 30.2.0 + babel-jest: 30.2.0(@babel/core@7.29.7) + chalk: 4.1.2 + ci-info: 4.4.0 + deepmerge: 4.3.1 + glob: 10.5.0 + graceful-fs: 4.2.11 + jest-circus: 30.2.0(babel-plugin-macros@3.1.0) + jest-docblock: 30.2.0 + jest-environment-node: 30.2.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.2.0 + jest-runner: 30.2.0 + jest-util: 30.2.0 + jest-validate: 30.2.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 30.2.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 20.19.37 + ts-node: 10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.9.3)(typescript@5.9.3) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + optional: true + + jest-config@30.2.0(@types/node@25.9.3)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.9.3)(typescript@5.8.3)): dependencies: '@babel/core': 7.29.7 '@jest/get-type': 30.1.0 @@ -32122,7 +32501,41 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 25.9.3 - ts-node: 10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3) + ts-node: 10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.9.3)(typescript@5.8.3) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + optional: true + + jest-config@30.2.0(@types/node@25.9.3)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.9.3)(typescript@5.9.3)): + dependencies: + '@babel/core': 7.29.7 + '@jest/get-type': 30.1.0 + '@jest/pattern': 30.0.1 + '@jest/test-sequencer': 30.2.0 + '@jest/types': 30.2.0 + babel-jest: 30.2.0(@babel/core@7.29.7) + chalk: 4.1.2 + ci-info: 4.4.0 + deepmerge: 4.3.1 + glob: 10.5.0 + graceful-fs: 4.2.11 + jest-circus: 30.2.0(babel-plugin-macros@3.1.0) + jest-docblock: 30.2.0 + jest-environment-node: 30.2.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.2.0 + jest-runner: 30.2.0 + jest-util: 30.2.0 + jest-validate: 30.2.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 30.2.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 25.9.3 + ts-node: 10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.9.3)(typescript@5.9.3) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -32903,6 +33316,22 @@ snapshots: - ts-node optional: true + jest@30.2.0(@types/node@20.12.8)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.8.3)): + dependencies: + '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.8.3)) + '@jest/types': 30.2.0 + import-local: 3.2.0 + jest-cli: 30.2.0(@types/node@20.12.8)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.8.3)) + optionalDependencies: + node-notifier: 9.0.1 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - esbuild-register + - supports-color + - ts-node + optional: true + jest@30.2.0(@types/node@20.12.8)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)): dependencies: '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)) @@ -32919,12 +33348,12 @@ snapshots: - ts-node optional: true - jest@30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)): + jest@30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.19.37)(typescript@5.8.3)): dependencies: - '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)) + '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.19.37)(typescript@5.8.3)) '@jest/types': 30.2.0 import-local: 3.2.0 - jest-cli: 30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)) + jest-cli: 30.2.0(@types/node@20.19.37)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.19.37)(typescript@5.8.3)) optionalDependencies: node-notifier: 9.0.1 transitivePeerDependencies: @@ -32935,12 +33364,28 @@ snapshots: - ts-node optional: true - jest@30.2.0(@types/node@25.9.3)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)): + jest@30.2.0(@types/node@25.9.3)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.9.3)(typescript@5.8.3)): dependencies: - '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)) + '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.9.3)(typescript@5.8.3)) '@jest/types': 30.2.0 import-local: 3.2.0 - jest-cli: 30.2.0(@types/node@25.9.3)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3)) + jest-cli: 30.2.0(@types/node@25.9.3)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.9.3)(typescript@5.8.3)) + optionalDependencies: + node-notifier: 9.0.1 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - esbuild-register + - supports-color + - ts-node + optional: true + + jest@30.2.0(@types/node@25.9.3)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.9.3)(typescript@5.9.3)): + dependencies: + '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.9.3)(typescript@5.9.3)) + '@jest/types': 30.2.0 + import-local: 3.2.0 + jest-cli: 30.2.0(@types/node@25.9.3)(babel-plugin-macros@3.1.0)(node-notifier@9.0.1)(ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.9.3)(typescript@5.9.3)) optionalDependencies: node-notifier: 9.0.1 transitivePeerDependencies: @@ -35397,13 +35842,13 @@ snapshots: postcss@8.5.10: dependencies: - nanoid: 3.3.11 + nanoid: 3.3.12 picocolors: 1.1.1 source-map-js: 1.2.1 postcss@8.5.12: dependencies: - nanoid: 3.3.11 + nanoid: 3.3.12 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -37781,7 +38226,7 @@ snapshots: lodash: 4.18.1 mkdirp: 0.5.6 mustache: 2.3.2 - nanoid: 3.3.11 + nanoid: 3.3.12 os-family: 1.1.0 pify: 2.3.0 pinkie: 2.0.4 @@ -37808,7 +38253,7 @@ snapshots: merge-stream: 1.0.1 mime: 1.4.1 mustache: 2.3.2 - nanoid: 3.3.11 + nanoid: 3.3.12 os-family: 1.1.0 parse5: 7.3.0 pinkie: 2.0.4 @@ -38353,6 +38798,27 @@ snapshots: optionalDependencies: '@swc/core': 1.15.30(@swc/helpers@0.5.21) + ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.8.3): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.12 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 20.12.8 + acorn: 8.17.0 + acorn-walk: 8.3.5 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.4 + make-error: 1.3.6 + typescript: 5.8.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + optionalDependencies: + '@swc/core': 1.15.30(@swc/helpers@0.5.21) + optional: true + ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.12.8)(typescript@5.9.3): dependencies: '@cspotcode/source-map-support': 0.8.1 @@ -38373,6 +38839,69 @@ snapshots: optionalDependencies: '@swc/core': 1.15.30(@swc/helpers@0.5.21) + ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@20.19.37)(typescript@5.8.3): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.12 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 20.19.37 + acorn: 8.17.0 + acorn-walk: 8.3.5 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.4 + make-error: 1.3.6 + typescript: 5.8.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + optionalDependencies: + '@swc/core': 1.15.30(@swc/helpers@0.5.21) + optional: true + + ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.9.3)(typescript@5.8.3): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.12 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 25.9.3 + acorn: 8.17.0 + acorn-walk: 8.3.5 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.4 + make-error: 1.3.6 + typescript: 5.8.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + optionalDependencies: + '@swc/core': 1.15.30(@swc/helpers@0.5.21) + optional: true + + ts-node@10.9.2(@swc/core@1.15.30(@swc/helpers@0.5.21))(@types/node@25.9.3)(typescript@5.9.3): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.12 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 25.9.3 + acorn: 8.17.0 + acorn-walk: 8.3.5 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.4 + make-error: 1.3.6 + typescript: 5.9.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + optionalDependencies: + '@swc/core': 1.15.30(@swc/helpers@0.5.21) + optional: true + tsc-alias@1.8.16: dependencies: chokidar: 3.6.0 From b404d2cef5cf80cbc1ec5fcd88f9e2e34a40bcb7 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Fri, 19 Jun 2026 23:38:55 +0200 Subject: [PATCH 19/23] refactor: use projectRequire --- .../executors/scss-build/executor.e2e.spec.ts | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts b/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts index 3cb469341d10..9a7be16182a7 100644 --- a/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts +++ b/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts @@ -5,11 +5,14 @@ import { ScssBuildExecutorSchema } from './schema'; import { createMockContext, createTempDir, cleanupTempDir } from '../../utils/test-utils'; import { writeFileText, writeJson, readFileText } from '../../utils'; -function createMockModules(workspaceRoot: string, projectRoot: string): void { - const projectNodeModules = path.join(projectRoot, 'node_modules', 'sass-embedded'); +function createMockModules(projectRoot: string): void { + const projectNodeModules = path.join(projectRoot, 'node_modules'); fs.mkdirSync(projectNodeModules, { recursive: true }); + + const sassEmbeddedDir = path.join(projectNodeModules, 'sass-embedded'); + fs.mkdirSync(sassEmbeddedDir, { recursive: true }); fs.writeFileSync( - path.join(projectNodeModules, 'index.js'), + path.join(sassEmbeddedDir, 'index.js'), [ 'class SassString {', ' constructor(value) { this.value = value; }', @@ -23,10 +26,7 @@ function createMockModules(workspaceRoot: string, projectRoot: string): void { 'utf8', ); - const workspaceNodeModules = path.join(workspaceRoot, 'node_modules'); - fs.mkdirSync(workspaceNodeModules, { recursive: true }); - - const postcssDir = path.join(workspaceNodeModules, 'postcss'); + const postcssDir = path.join(projectNodeModules, 'postcss'); fs.mkdirSync(postcssDir, { recursive: true }); fs.writeFileSync( path.join(postcssDir, 'index.js'), @@ -41,7 +41,7 @@ function createMockModules(workspaceRoot: string, projectRoot: string): void { 'utf8', ); - const autoprefixerDir = path.join(workspaceNodeModules, 'autoprefixer'); + const autoprefixerDir = path.join(projectNodeModules, 'autoprefixer'); fs.mkdirSync(autoprefixerDir, { recursive: true }); fs.writeFileSync( path.join(autoprefixerDir, 'index.js'), @@ -49,7 +49,7 @@ function createMockModules(workspaceRoot: string, projectRoot: string): void { 'utf8', ); - const cleanCssDir = path.join(workspaceNodeModules, 'clean-css'); + const cleanCssDir = path.join(projectNodeModules, 'clean-css'); fs.mkdirSync(cleanCssDir, { recursive: true }); fs.writeFileSync( path.join(cleanCssDir, 'index.js'), @@ -65,7 +65,7 @@ function createMockModules(workspaceRoot: string, projectRoot: string): void { 'utf8', ); - const chokidarDir = path.join(workspaceNodeModules, 'chokidar'); + const chokidarDir = path.join(projectNodeModules, 'chokidar'); fs.mkdirSync(chokidarDir, { recursive: true }); fs.writeFileSync( path.join(chokidarDir, 'index.js'), @@ -129,7 +129,7 @@ async function setupProjectStructure(workspaceRoot: string): Promise { '.generic-$COLOR { color: red; }', ); - createMockModules(workspaceRoot, projectRoot); + createMockModules(projectRoot); return projectRoot; } From 06de729d09448166ab78b8eedabc94dc9f32c4a8 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Fri, 19 Jun 2026 23:42:50 +0200 Subject: [PATCH 20/23] refactor add inputs for actual nx caching --- packages/devextreme-scss/project.json | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/devextreme-scss/project.json b/packages/devextreme-scss/project.json index aa76b5144363..f7f65a12a457 100644 --- a/packages/devextreme-scss/project.json +++ b/packages/devextreme-scss/project.json @@ -55,7 +55,8 @@ "{projectRoot}/fonts/**/*", "{projectRoot}/icons/**/*", "{projectRoot}/images/**/*", - "{projectRoot}/scss/**/*" + "{projectRoot}/scss/**/*", + "{workspaceRoot}/packages/devextreme/package.json" ], "outputs": [ "{projectRoot}/scss/bundles", @@ -73,7 +74,9 @@ "{projectRoot}/fonts/**/*", "{projectRoot}/icons/**/*", "{projectRoot}/images/**/*", - "{projectRoot}/scss/**/*" + "{projectRoot}/scss/**/*", + "{workspaceRoot}/packages/devextreme/package.json", + "{workspaceRoot}/packages/devextreme-themebuilder/src/data/clean-css-options.json" ], "outputs": [ "{projectRoot}/scss/bundles", @@ -95,7 +98,8 @@ "{projectRoot}/fonts/**/*", "{projectRoot}/icons/**/*", "{projectRoot}/images/**/*", - "{projectRoot}/scss/**/*" + "{projectRoot}/scss/**/*", + "{workspaceRoot}/packages/devextreme/package.json" ], "outputs": [ "{projectRoot}/scss/bundles", @@ -119,7 +123,9 @@ "{projectRoot}/fonts/**/*", "{projectRoot}/icons/**/*", "{projectRoot}/images/**/*", - "{projectRoot}/scss/**/*" + "{projectRoot}/scss/**/*", + "{workspaceRoot}/packages/devextreme/package.json", + "{workspaceRoot}/packages/devextreme-themebuilder/src/data/clean-css-options.json" ], "outputs": [ "{projectRoot}/scss/bundles", From 06edf434e42c2375385d8000a04c266de6b7b080 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Sat, 20 Jun 2026 00:01:50 +0200 Subject: [PATCH 21/23] fix e2e --- .../executors/scss-build/executor.e2e.spec.ts | 43 ++++++++++++------- .../executors/scss-build/scss-build.impl.ts | 26 ++++++----- 2 files changed, 42 insertions(+), 27 deletions(-) diff --git a/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts b/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts index 9a7be16182a7..6ccde497d886 100644 --- a/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts +++ b/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts @@ -5,14 +5,20 @@ import { ScssBuildExecutorSchema } from './schema'; import { createMockContext, createTempDir, cleanupTempDir } from '../../utils/test-utils'; import { writeFileText, writeJson, readFileText } from '../../utils'; +function writeMockPackage(nodeModulesDir: string, packageName: string, mainFile: string): void { + const packageDir = path.join(nodeModulesDir, packageName); + fs.mkdirSync(packageDir, { recursive: true }); + fs.writeFileSync(path.join(packageDir, 'package.json'), JSON.stringify({ main: mainFile }), 'utf8'); + fs.writeFileSync(path.join(packageDir, mainFile), '', 'utf8'); +} + function createMockModules(projectRoot: string): void { const projectNodeModules = path.join(projectRoot, 'node_modules'); fs.mkdirSync(projectNodeModules, { recursive: true }); - const sassEmbeddedDir = path.join(projectNodeModules, 'sass-embedded'); - fs.mkdirSync(sassEmbeddedDir, { recursive: true }); + writeMockPackage(projectNodeModules, 'sass-embedded', 'index.js'); fs.writeFileSync( - path.join(sassEmbeddedDir, 'index.js'), + path.join(projectNodeModules, 'sass-embedded', 'index.js'), [ 'class SassString {', ' constructor(value) { this.value = value; }', @@ -26,10 +32,9 @@ function createMockModules(projectRoot: string): void { 'utf8', ); - const postcssDir = path.join(projectNodeModules, 'postcss'); - fs.mkdirSync(postcssDir, { recursive: true }); + writeMockPackage(projectNodeModules, 'postcss', 'index.js'); fs.writeFileSync( - path.join(postcssDir, 'index.js'), + path.join(projectNodeModules, 'postcss', 'index.js'), [ 'module.exports = function postcss() {', ' return {', @@ -41,18 +46,16 @@ function createMockModules(projectRoot: string): void { 'utf8', ); - const autoprefixerDir = path.join(projectNodeModules, 'autoprefixer'); - fs.mkdirSync(autoprefixerDir, { recursive: true }); + writeMockPackage(projectNodeModules, 'autoprefixer', 'index.js'); fs.writeFileSync( - path.join(autoprefixerDir, 'index.js'), + path.join(projectNodeModules, 'autoprefixer', 'index.js'), 'module.exports = function autoprefixer() { return { postcssPlugin: "autoprefixer" }; };', 'utf8', ); - const cleanCssDir = path.join(projectNodeModules, 'clean-css'); - fs.mkdirSync(cleanCssDir, { recursive: true }); + writeMockPackage(projectNodeModules, 'clean-css', 'index.js'); fs.writeFileSync( - path.join(cleanCssDir, 'index.js'), + path.join(projectNodeModules, 'clean-css', 'index.js'), [ 'module.exports = class CleanCss {', ' constructor(options) { this.options = options || {}; }', @@ -65,10 +68,9 @@ function createMockModules(projectRoot: string): void { 'utf8', ); - const chokidarDir = path.join(projectNodeModules, 'chokidar'); - fs.mkdirSync(chokidarDir, { recursive: true }); + writeMockPackage(projectNodeModules, 'chokidar', 'index.js'); fs.writeFileSync( - path.join(chokidarDir, 'index.js'), + path.join(projectNodeModules, 'chokidar', 'index.js'), [ 'module.exports = {', ' watch: function watch() {', @@ -90,7 +92,16 @@ async function setupProjectStructure(workspaceRoot: string): Promise { fs.mkdirSync(buildDir, { recursive: true }); await writeJson(path.join(workspaceRoot, 'package.json'), { name: 'workspace' }); - await writeJson(path.join(projectRoot, 'package.json'), { name: 'devextreme-scss' }); + await writeJson(path.join(projectRoot, 'package.json'), { + name: 'devextreme-scss', + devDependencies: { + 'sass-embedded': '1.0.0', + postcss: '8.0.0', + autoprefixer: '10.0.0', + 'clean-css': '5.0.0', + chokidar: '5.0.0', + }, + }); await writeJson(path.join(projectRoot, 'build', 'clean-css-options.json'), { profile: 'all' }); diff --git a/packages/nx-infra-plugin/src/executors/scss-build/scss-build.impl.ts b/packages/nx-infra-plugin/src/executors/scss-build/scss-build.impl.ts index 1b17e2b00cb4..d66a0072a904 100644 --- a/packages/nx-infra-plugin/src/executors/scss-build/scss-build.impl.ts +++ b/packages/nx-infra-plugin/src/executors/scss-build/scss-build.impl.ts @@ -32,15 +32,6 @@ interface BuildDependencies { sass: any; postcss: any; autoprefixer: (options?: { overrideBrowserslist?: string[] }) => any; - chokidar: { - watch: ( - paths: string | string[], - options?: Record, - ) => { - on: (event: string, handler: (...args: any[]) => void) => unknown; - close: () => Promise | void; - }; - }; CleanCss: new (options: unknown) => { minify: (input: string) => { styles: string } }; themeOptions: { getThemes: () => Array<[string, string, string, string?]> }; cleanCssSanitizeOptions: unknown; @@ -125,7 +116,6 @@ function loadDependencies(projectRoot: string): BuildDependencies { sass: projectRequire('sass-embedded'), postcss: projectRequire('postcss'), autoprefixer: projectRequire('autoprefixer'), - chokidar: projectRequire('chokidar'), CleanCss: projectRequire('clean-css'), themeOptions: projectRequire(path.resolve(projectRoot, 'build/theme-options.cjs')) as { getThemes: () => Array<[string, string, string, string?]>; @@ -284,6 +274,19 @@ async function runSingleBuild( } } +function loadChokidar(projectRoot: string): { + watch: ( + paths: string | string[], + options?: Record, + ) => { + on: (event: string, handler: (...args: any[]) => void) => unknown; + close: () => Promise | void; + }; +} { + const projectRequire = createRequire(path.join(projectRoot, 'package.json')); + return projectRequire('chokidar'); +} + async function runWatchBuild( projectRoot: string, options: ScssBuildExecutorSchema, @@ -347,7 +350,8 @@ async function runWatchBuild( }, 200); }; - const watcher = deps.chokidar.watch(path.join(watchDir, '**/*.scss'), { + const chokidar = loadChokidar(projectRoot); + const watcher = chokidar.watch(path.join(watchDir, '**/*.scss'), { ignoreInitial: true, }); watcher.on('all', scheduleRebuild); From 84f317fad40ae91a4a492dcce6e2740507d687b9 Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Sat, 20 Jun 2026 00:23:39 +0200 Subject: [PATCH 22/23] fix lint --- .../src/executors/scss-build/executor.e2e.spec.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts b/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts index 6ccde497d886..eec75cfae5ee 100644 --- a/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts +++ b/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts @@ -8,7 +8,11 @@ import { writeFileText, writeJson, readFileText } from '../../utils'; function writeMockPackage(nodeModulesDir: string, packageName: string, mainFile: string): void { const packageDir = path.join(nodeModulesDir, packageName); fs.mkdirSync(packageDir, { recursive: true }); - fs.writeFileSync(path.join(packageDir, 'package.json'), JSON.stringify({ main: mainFile }), 'utf8'); + fs.writeFileSync( + path.join(packageDir, 'package.json'), + JSON.stringify({ main: mainFile }), + 'utf8', + ); fs.writeFileSync(path.join(packageDir, mainFile), '', 'utf8'); } From 403f42912ad341ab01392e2f04f05d4e37e923fa Mon Sep 17 00:00:00 2001 From: Aliullov Vlad Date: Mon, 22 Jun 2026 09:32:19 +0200 Subject: [PATCH 23/23] fix source resolve ib CI mode --- .../executors/scss-build/executor.e2e.spec.ts | 18 ++++++++++ .../executors/scss-build/scss-build.impl.ts | 33 ++++++++++++------- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts b/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts index eec75cfae5ee..10c6d1e9a037 100644 --- a/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts +++ b/packages/nx-infra-plugin/src/executors/scss-build/executor.e2e.spec.ts @@ -218,4 +218,22 @@ describe('ScssBuildExecutor E2E', () => { expect(fs.existsSync(path.join(projectRoot, 'scss', 'bundles', 'dx.common.scss'))).toBe(true); }); + + it('fails in ci mode when a configured bundle source is missing', async () => { + await setupProjectStructure(tempDir); + const context = createMockContext({ + root: tempDir, + projectName: 'devextreme-scss', + projectRoot: 'packages/devextreme-scss', + }); + + const options: ScssBuildExecutorSchema = { + mode: 'ci', + devBundles: ['light', 'missing.bundle'], + cssOutputDir: './artifacts/css', + }; + const result = await executor(options, context); + + expect(result.success).toBe(false); + }); }); diff --git a/packages/nx-infra-plugin/src/executors/scss-build/scss-build.impl.ts b/packages/nx-infra-plugin/src/executors/scss-build/scss-build.impl.ts index d66a0072a904..a505502180a2 100644 --- a/packages/nx-infra-plugin/src/executors/scss-build/scss-build.impl.ts +++ b/packages/nx-infra-plugin/src/executors/scss-build/scss-build.impl.ts @@ -230,15 +230,17 @@ function resolveSourcesByBundleNames( bundleNames: string[], ): string[] { const resolvedBundlesDir = path.resolve(projectRoot, bundlesDir); - const sources: string[] = []; - - for (const bundleName of bundleNames) { - const source = path.join(resolvedBundlesDir, `dx.${bundleName}.scss`); - if (fs.existsSync(source)) { - sources.push(source); - } else { - logger.warn(`${source} file does not exist`); - } + return bundleNames.map((bundleName) => path.join(resolvedBundlesDir, `dx.${bundleName}.scss`)); +} + +function assertCiSourcesAvailable(sources: string[]): string[] { + const missingSources = sources.filter((source) => !fs.existsSync(source)); + if (missingSources.length > 0) { + throw new Error(`Missing SCSS bundle source(s) in CI mode: ${missingSources.join(', ')}`); + } + + if (sources.length === 0) { + throw new Error('No SCSS bundle sources configured for CI mode'); } return sources; @@ -265,10 +267,13 @@ async function runSingleBuild( await ensureDir(cssOutputDir); const sources = await resolveSourceFiles(projectRoot, options); - const existingSources = sources.filter((source) => fs.existsSync(source)); + const sourcesToCompile = + options.mode === 'ci' + ? assertCiSourcesAvailable(sources) + : sources.filter((source) => fs.existsSync(source)); const minifyProfile: MinifyProfile = options.mode === 'ci' ? 'ci' : 'all'; - for (const source of existingSources) { + for (const source of sourcesToCompile) { logger.verbose(`Compiling ${source}`); await compileFile(source, cssOutputDir, minifyProfile, deps, projectRoot); } @@ -302,7 +307,11 @@ async function runWatchBuild( await generateScssBundles(projectRoot, bundlesDir, deps); await ensureDir(cssOutputDir); - const sources = resolveSourcesByBundleNames(projectRoot, bundlesDir, watchBundleNames); + const resolvedSources = resolveSourcesByBundleNames(projectRoot, bundlesDir, watchBundleNames); + const sources = + options.mode === 'ci' + ? assertCiSourcesAvailable(resolvedSources) + : resolvedSources.filter((source) => fs.existsSync(source)); for (const source of sources) { await compileFile(source, cssOutputDir, minifyProfile, deps, projectRoot); }