From 8267c00c4291419f5950ead9480d16c0e2a51e76 Mon Sep 17 00:00:00 2001 From: Shaun Evening Date: Thu, 11 May 2023 19:19:28 -0400 Subject: [PATCH] Add advanced config options for css modules (#39) * Add configuration options for css-modules * Make vite config TypeScript * Add vitest * Add tests for webpack rule generators * Create serializer for webpack config snapshots * Add unit tests to CI --- .github/workflows/test.yml | 29 +++ package.json | 4 +- src/types.ts | 2 +- src/webpack/css/webpack.test.ts | 313 +++++++++++++++++++++++++ src/webpack/css/webpack.ts | 27 ++- src/webpack/less/webpack.test.ts | 348 ++++++++++++++++++++++++++++ src/webpack/less/webpack.ts | 36 ++- src/webpack/scss/webpack.test.ts | 378 +++++++++++++++++++++++++++++++ src/webpack/scss/webpack.ts | 29 ++- vite.config.js => vite.config.ts | 1 + vitest.config.ts | 13 ++ vitest/setup.ts | 24 ++ yarn.lock | 373 +++++++++++++++++++++++++++++- 13 files changed, 1547 insertions(+), 30 deletions(-) create mode 100644 .github/workflows/test.yml create mode 100644 src/webpack/css/webpack.test.ts create mode 100644 src/webpack/less/webpack.test.ts create mode 100644 src/webpack/scss/webpack.test.ts rename vite.config.js => vite.config.ts (99%) create mode 100644 vitest.config.ts create mode 100644 vitest/setup.ts diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..2e50344 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,29 @@ +name: Run test suite + +on: + push: + branches: + - main + - next + pull_request: + types: [opened, synchronize, reopened] + +jobs: + build: + name: Unit tests + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [16.x] + + steps: + - uses: actions/checkout@v3 + - name: Set node version to ${{ matrix.node_version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node_version }} + cache: "npm" + - name: Install dependencies + run: yarn + - name: Run Vitest + run: yarn test --run diff --git a/package.json b/package.json index f2c7804..1f499ff 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,8 @@ "prebuild": "yarn clean", "build": "tsup", "build:watch": "yarn build --watch", - "test": "echo \"Error: no test specified\" && exit 1", + "test": "vitest", + "test:coverage": "vitest run --coverage", "storybook": "storybook dev -p 6006", "start": "concurrently \"yarn build:watch\" \"yarn storybook -- --quiet\"", "build-storybook": "storybook build", @@ -103,6 +104,7 @@ "tsup": "^6.7.0", "typescript": "^4.9.0", "vite": "^4.2.1", + "vitest": "^0.31.0", "zx": "^1.14.1" }, "peerDependencies": { diff --git a/src/types.ts b/src/types.ts index f6e8c80..f618fa6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -8,7 +8,7 @@ interface LessConfig { export interface AddonStylingOptions { cssBuildRule?: RuleSetRule; - cssModules?: boolean; + cssModules?: boolean | Record; less?: LessConfig; lessBuildRule?: RuleSetRule; postCss?: boolean | object; diff --git a/src/webpack/css/webpack.test.ts b/src/webpack/css/webpack.test.ts new file mode 100644 index 0000000..ecb16d2 --- /dev/null +++ b/src/webpack/css/webpack.test.ts @@ -0,0 +1,313 @@ +import { describe, it } from "vitest"; +import { + isRuleForCSS, + buildStyleLoader, + buildCssModuleRules, + buildCssLoader, + buildPostCssLoader, + patchOrAddCssRule, + CSS_FILE_REGEX, +} from "./webpack"; + +import type { RuleSetRule, Configuration as WebpackConfig } from "webpack"; + +describe("WEBPACK/CSS: configuration builders for css files", () => { + describe("UTILITY: isRuleForCSS", () => { + it("TRUE: it should return true when given a webpack rule for css files", async ({ + expect, + }) => { + const webpackRule: RuleSetRule = { + test: /\.css$/i, + use: [], + }; + const result = isRuleForCSS(webpackRule); + + expect(result).toBeTruthy(); + }); + + it("FALSE: it should return false when given a webpack rule for other file types", async ({ + expect, + }) => { + const someWebpackRule: RuleSetRule = { + test: /\.svg$/i, + use: [], + }; + const someResult = isRuleForCSS(someWebpackRule); + + const otherWebpackRule: RuleSetRule = { + test: /\.svg$/i, + use: [], + }; + const otherResult = isRuleForCSS(otherWebpackRule); + + expect(someResult).toBeFalsy(); + expect(otherResult).toBeFalsy(); + }); + }); + + describe("UTILITY: buildStyleLoader", () => { + it("it should return a style-loader object", async ({ expect }) => { + const result = buildStyleLoader({}); + + expect(result).toHaveProperty("loader"); + expect(result.loader).toEqual(require.resolve("style-loader")); + }); + }); + + describe("UTILITY: buildCssModuleRules", () => { + it("NO MODULE RULES: it should return an empty object when no module rules are given", async ({ + expect, + }) => { + const result = buildCssModuleRules({}); + + expect(result).toMatchObject({}); + }); + + it('BASIC: it should return a minimal config for css modules when given "{ cssModules: true }"', async ({ + expect, + }) => { + const result = buildCssModuleRules({ cssModules: true }); + + expect(result).toMatchObject({ modules: { auto: true } }); + }); + + it("CUSTOM: it should return a custom config for css modules when given custom rules", async ({ + expect, + }) => { + const customRules = { + auto: true, + namedExport: true, + getLocalIdent: (context, localIdentName, localName, options) => { + return "whatever_random_class_name"; + }, + }; + + const result = buildCssModuleRules({ cssModules: { ...customRules } }); + + expect(result).toMatchObject({ modules: customRules }); + }); + }); + + describe("UTILITY: buildCssLoader", () => { + const BASE_LOADER = { + loader: require.resolve("css-loader"), + options: {}, + }; + + it("NO ADDON OPTIONS: it should return a css-loader with no options", async ({ + expect, + }) => { + const result = buildCssLoader({}); + + expect(result).toMatchObject(BASE_LOADER); + }); + + it("POSTCSS ENABLED: it should configure the css-loader to handle imports AFTER postcss", async ({ + expect, + }) => { + const expected = { + ...BASE_LOADER, + options: { importLoaders: 1 }, + }; + + const result = buildCssLoader({ + postCss: { implementation: require.resolve("postcss") }, + }); + + expect(result).toMatchObject(expected); + }); + + it("CSS MODULES ENABLED: it should configure the css-loader to support css-modules", async ({ + expect, + }) => { + const expected = { + ...BASE_LOADER, + options: { modules: { auto: true } }, + }; + + const result = buildCssLoader({ + cssModules: true, + }); + + expect(result).toMatchObject(expected); + }); + + it("CSS MODULES & POSTCSS ENABLED: it should configure the css-loader to support css-modules and postcss", async ({ + expect, + }) => { + const expected = { + ...BASE_LOADER, + options: { modules: { auto: true }, importLoaders: 1 }, + }; + + const result = buildCssLoader({ + cssModules: true, + postCss: { implementation: require.resolve("postcss") }, + }); + + expect(result).toMatchObject(expected); + }); + }); + + describe("UTILITY: buildPostCssLoader", () => { + it("it should return a postcss-loader object", async ({ expect }) => { + const result = buildPostCssLoader({}); + + expect(result).toHaveProperty("loader"); + expect(result.loader).toEqual(require.resolve("postcss-loader")); + }); + + it("IMPLEMENTATION: it should return a postcss-loader with a given implementation of postcss", async ({ + expect, + }) => { + const result = buildPostCssLoader({ + postCss: { implementation: require.resolve("postcss") }, + }); + + expect(result).toHaveProperty("loader"); + expect(result.loader).toEqual(require.resolve("postcss-loader")); + }); + }); + + describe("UTILITY: patchOrAddCssRule", () => { + const EXISTING_CSS_RULES = { + test: CSS_FILE_REGEX, + use: ["style-loader", "css-loader"], + }; + + const createTestWebpackConfig = (id: string): WebpackConfig => ({ + name: `testConfig-${id}`, + module: { + rules: [EXISTING_CSS_RULES], + }, + }); + + it("NO CONFIGURATION: it should leave existing css rules alone if no configuration is given", async ({ + expect, + }) => { + const config = createTestWebpackConfig("default"); + + patchOrAddCssRule(config, {}); + + expect(config.module.rules).toContainEqual(EXISTING_CSS_RULES); + }); + + it("ENABLE POSTCSS: it should replace existing css rules to enable postcss", async ({ + expect, + }) => { + const config = createTestWebpackConfig("postcss"); + const addonOptions = { + postCss: { implementation: require.resolve("postcss") }, + }; + + patchOrAddCssRule(config, addonOptions); + + expect(config.module.rules.length).toEqual(1); + expect(config.module.rules).not.toContain(EXISTING_CSS_RULES); + expect(config.module.rules[0].sideEffects).toBeTruthy(); + expect(config.module.rules[0].test.test("test.css")).toBeTruthy(); + expect(config.module.rules[0].use).toMatchInlineSnapshot(` + [ + { + "loader": "path/to/project/node_modules/style-loader/dist/cjs.js", + }, + { + "loader": "path/to/project/node_modules/css-loader/dist/cjs.js", + "options": { + "importLoaders": 1, + }, + }, + { + "loader": "path/to/project/node_modules/postcss-loader/dist/cjs.js", + "options": { + "implementation": "path/to/project/node_modules/postcss/lib/postcss.js", + }, + }, + ] + `); + }); + + it("ENABLE CSS MODULES: it should replace existing css rules to enable css modules", async ({ + expect, + }) => { + const config = createTestWebpackConfig("postcss"); + + patchOrAddCssRule(config, { + cssModules: true, + }); + + expect(config.module.rules.length).toEqual(1); + expect(config.module.rules).not.toContain(EXISTING_CSS_RULES); + expect(config.module.rules[0].use).toMatchInlineSnapshot(` + [ + { + "loader": "path/to/project/node_modules/style-loader/dist/cjs.js", + }, + { + "loader": "path/to/project/node_modules/css-loader/dist/cjs.js", + "options": { + "modules": { + "auto": true, + }, + }, + }, + ] + `); + }); + + it("ENABLE POSTCSS & CSS MODULES: it should replace existing css rules to enable postcss & css modules", async ({ + expect, + }) => { + const config = createTestWebpackConfig("complex"); + + patchOrAddCssRule(config, { + postCss: { implementation: require.resolve("postcss") }, + cssModules: true, + }); + + expect(config.module.rules.length).toEqual(1); + expect(config.module.rules[0]?.sideEffects).toBeTruthy(); + expect(config.module.rules).not.toContain(EXISTING_CSS_RULES); + expect(config.module.rules[0].use).toMatchInlineSnapshot(` + [ + { + "loader": "path/to/project/node_modules/style-loader/dist/cjs.js", + }, + { + "loader": "path/to/project/node_modules/css-loader/dist/cjs.js", + "options": { + "importLoaders": 1, + "modules": { + "auto": true, + }, + }, + }, + { + "loader": "path/to/project/node_modules/postcss-loader/dist/cjs.js", + "options": { + "implementation": "path/to/project/node_modules/postcss/lib/postcss.js", + }, + }, + ] + `); + }); + + it("OVERRIDE RULE: it should replace existing css rules with the given rule", async ({ + expect, + }) => { + const RULE_OVERRIDE: RuleSetRule = { + test: CSS_FILE_REGEX, + use: ["custom-loader", { loader: "css-loader", options: {} }], + }; + + const config = createTestWebpackConfig("override"); + + patchOrAddCssRule(config, { + cssBuildRule: RULE_OVERRIDE, + }); + + expect(config.module.rules.length).toEqual(1); + expect(config.module.rules).toContain(RULE_OVERRIDE); + }); + }); +}); diff --git a/src/webpack/css/webpack.ts b/src/webpack/css/webpack.ts index 84a5f26..28ba7de 100644 --- a/src/webpack/css/webpack.ts +++ b/src/webpack/css/webpack.ts @@ -1,18 +1,31 @@ import type { RuleSetRule, Configuration as WebpackConfig } from "webpack"; import type { AddonStylingOptions } from "../../types"; -const isRuleForCSS = (rule: RuleSetRule) => +export const isRuleForCSS = (rule: RuleSetRule) => typeof rule !== "string" && rule.test instanceof RegExp && rule.test.test("test.css"); -const buildStyleLoader = (options: AddonStylingOptions) => ({ +export const buildStyleLoader = (options: AddonStylingOptions) => ({ loader: require.resolve("style-loader"), }); -const buildCssLoader = ({ cssModules, postCss }: AddonStylingOptions) => { +export const buildCssModuleRules = ({ cssModules }: AddonStylingOptions) => { + if (!cssModules) { + return {}; + } + + return typeof cssModules === "object" + ? { modules: { ...cssModules } } + : { modules: { auto: true } }; +}; + +export const buildCssLoader = ({ + cssModules, + postCss, +}: AddonStylingOptions) => { const importSettings = postCss ? { importLoaders: 1 } : {}; - const moduleSettings = cssModules ? { modules: { auto: true } } : {}; + const moduleSettings = buildCssModuleRules({ cssModules }); return { loader: require.resolve("css-loader"), @@ -23,7 +36,7 @@ const buildCssLoader = ({ cssModules, postCss }: AddonStylingOptions) => { }; }; -const buildPostCssLoader = ({ postCss }: AddonStylingOptions) => { +export const buildPostCssLoader = ({ postCss }: AddonStylingOptions) => { const implementationOptions = typeof postCss === "object" ? { ...postCss } : {}; @@ -35,8 +48,8 @@ const buildPostCssLoader = ({ postCss }: AddonStylingOptions) => { }; }; -const CSS_FILE_REGEX = /\.css$/; -const buildCssRule = (options: AddonStylingOptions): RuleSetRule => { +export const CSS_FILE_REGEX = /\.css$/; +export const buildCssRule = (options: AddonStylingOptions): RuleSetRule => { if (options.cssBuildRule) return options.cssBuildRule; const buildRule = [ diff --git a/src/webpack/less/webpack.test.ts b/src/webpack/less/webpack.test.ts new file mode 100644 index 0000000..eeaf1d6 --- /dev/null +++ b/src/webpack/less/webpack.test.ts @@ -0,0 +1,348 @@ +import { describe, it } from "vitest"; +import { + isRuleForLESS, + buildStyleLoader, + buildCssModuleRules, + buildCssLoader, + buildPostCssLoader, + buildLessLoader, + patchOrAddLessRule, + LESS_FILE_REGEX, +} from "./webpack"; + +import type { RuleSetRule, Configuration as WebpackConfig } from "webpack"; + +describe("WEBPACK/LESS: configuration builders for less files", () => { + describe("UTILITY: isRuleForLESS", () => { + it("TRUE: it should return true when given a webpack rule for less files", async ({ + expect, + }) => { + const webpackRule: RuleSetRule = { + test: /\.less$/i, + use: [], + }; + const result = isRuleForLESS(webpackRule); + + expect(result).toBeTruthy(); + }); + + it("FALSE: it should return false when given a webpack rule for other file types", async ({ + expect, + }) => { + const someWebpackRule: RuleSetRule = { + test: /\.svg$/i, + use: [], + }; + const someResult = isRuleForLESS(someWebpackRule); + + const otherWebpackRule: RuleSetRule = { + test: /\.svg$/i, + use: [], + }; + const otherResult = isRuleForLESS(otherWebpackRule); + + expect(someResult).toBeFalsy(); + expect(otherResult).toBeFalsy(); + }); + }); + + describe("UTILITY: buildStyleLoader", () => { + it("LOADER: it should return a style-loader object", async ({ expect }) => { + const result = buildStyleLoader({}); + + expect(result).toHaveProperty("loader"); + expect(result.loader).toEqual(require.resolve("style-loader")); + }); + }); + + describe("UTILITY: buildCssModuleRules", () => { + it("NO MODULE RULES: it should return an empty object when no module rules are given", async ({ + expect, + }) => { + const result = buildCssModuleRules({}); + + expect(result).toMatchObject({}); + }); + + it('BASIC: it should return a minimal config for css modules when given "{ cssModules: true }"', async ({ + expect, + }) => { + const result = buildCssModuleRules({ cssModules: true }); + + expect(result).toMatchObject({ modules: { auto: true } }); + }); + + it("CUSTOM: it should return a custom config for css modules when given custom rules", async ({ + expect, + }) => { + const customRules = { + auto: true, + namedExport: true, + getLocalIdent: (context, localIdentName, localName, options) => { + return "whatever_random_class_name"; + }, + }; + + const result = buildCssModuleRules({ cssModules: { ...customRules } }); + + expect(result).toMatchObject({ modules: customRules }); + }); + }); + + describe("UTILITY: buildCssLoader", () => { + const BASE_LOADER = { + loader: require.resolve("css-loader"), + options: {}, + }; + + it("NO ADDON OPTIONS: it should return a css-loader with no options", async ({ + expect, + }) => { + const result = buildCssLoader({}); + + expect(result).toMatchObject(BASE_LOADER); + }); + + it("POSTCSS ENABLED: it should configure the css-loader to handle imports AFTER postcss", async ({ + expect, + }) => { + const expected = { + ...BASE_LOADER, + options: { importLoaders: 2 }, + }; + + const result = buildCssLoader({ + postCss: { implementation: 'require.resolve("postcss")' }, + }); + + expect(result).toMatchObject(expected); + }); + + it("CSS MODULES ENABLED: it should configure the css-loader to support css-modules", async ({ + expect, + }) => { + const expected = { + ...BASE_LOADER, + options: { modules: { auto: true } }, + }; + + const result = buildCssLoader({ + cssModules: true, + }); + + expect(result).toMatchObject(expected); + }); + + it("CSS MODULES & POSTCSS ENABLED: it should configure the css-loader to support css-modules and postcss", async ({ + expect, + }) => { + const expected = { + ...BASE_LOADER, + options: { modules: { auto: true }, importLoaders: 2 }, + }; + + const result = buildCssLoader({ + cssModules: true, + postCss: { implementation: 'require.resolve("postcss")' }, + }); + + expect(result).toMatchObject(expected); + }); + }); + + describe("UTILITY: buildPostCssLoader", () => { + it("it should return a postcss-loader object", async ({ expect }) => { + const result = buildPostCssLoader({}); + + expect(result).toHaveProperty("loader"); + expect(result.loader).toEqual(require.resolve("postcss-loader")); + }); + + it("IMPLEMENTATION: it should return a postcss-loader with a given implementation of postcss", async ({ + expect, + }) => { + const result = buildPostCssLoader({ + postCss: { implementation: 'require.resolve("postcss")' }, + }); + + expect(result).toHaveProperty("loader"); + expect(result.loader).toEqual(require.resolve("postcss-loader")); + }); + }); + + describe("UTILITY: buildLessLoader", () => { + it("LOADER: it should return a sass-loader object", ({ expect }) => { + const result = buildLessLoader({ + sass: { implementation: 'require.resolve("less")' }, + }); + + expect(result).toHaveProperty("loader"); + expect(result.loader).toEqual(require.resolve("less-loader")); + }); + }); + + describe("UTILITY: patchOrAddLessRule", () => { + const EXISTING_LESS_RULES = { + test: LESS_FILE_REGEX, + use: ["style-loader", "css-loader"], + }; + + const createTestWebpackConfig = (id: string): WebpackConfig => ({ + name: `testConfig-${id}`, + module: { + rules: [EXISTING_LESS_RULES], + }, + }); + + it("NO CONFIGURATION: it should leave existing scss rules alone if no configuration is given", async ({ + expect, + }) => { + const config = createTestWebpackConfig("default"); + + patchOrAddLessRule(config, {}); + + expect(config.module.rules).toContainEqual(EXISTING_LESS_RULES); + }); + + it("ENABLE POSTCSS: it should replace existing scss rules to enable postcss", async ({ + expect, + }) => { + const config = createTestWebpackConfig("postcss"); + + patchOrAddLessRule(config, { + postCss: { implementation: 'require.resolve("postcss")' }, + less: { implementation: 'require.resolve("less")' }, + }); + + expect(config.module.rules.length).toEqual(1); + expect(config.module.rules).not.toContain(EXISTING_LESS_RULES); + expect(config.module.rules[0]?.use).toMatchInlineSnapshot(` + [ + { + "loader": "path/to/project/node_modules/style-loader/dist/cjs.js", + }, + { + "loader": "path/to/project/node_modules/css-loader/dist/cjs.js", + "options": { + "importLoaders": 2, + }, + }, + { + "loader": "path/to/project/node_modules/postcss-loader/dist/cjs.js", + "options": { + "implementation": "require.resolve(\\"postcss\\")", + }, + }, + { + "loader": "path/to/project/node_modules/less-loader/dist/cjs.js", + "options": { + "implementation": "require.resolve(\\"less\\")", + "sourceMap": true, + }, + }, + ] + `); + }); + + it("ENABLE CSS MODULES: it should replace existing scss rules to enable css modules", async ({ + expect, + }) => { + const config = createTestWebpackConfig("postcss"); + + patchOrAddLessRule(config, { + cssModules: true, + less: { implementation: 'require.resolve("less")' }, + }); + + expect(config.module.rules.length).toEqual(1); + expect(config.module.rules).not.toContain(EXISTING_LESS_RULES); + expect(config.module.rules[0]?.use).toMatchInlineSnapshot(` + [ + { + "loader": "path/to/project/node_modules/style-loader/dist/cjs.js", + }, + { + "loader": "path/to/project/node_modules/css-loader/dist/cjs.js", + "options": { + "importLoaders": 1, + "modules": { + "auto": true, + }, + }, + }, + { + "loader": "path/to/project/node_modules/less-loader/dist/cjs.js", + "options": { + "implementation": "require.resolve(\\"less\\")", + "sourceMap": true, + }, + }, + ] + `); + }); + + it("ENABLE POSTCSS & CSS MODULES: it should replace existing scss rules to enable postcss & css modules", async ({ + expect, + }) => { + const config = createTestWebpackConfig("complex"); + + patchOrAddLessRule(config, { + postCss: { implementation: 'require.resolve("postcss")' }, + less: { implementation: 'require.resolve("less")' }, + cssModules: { auto: true, namedExport: true }, + }); + + expect(config.module.rules.length).toEqual(1); + expect(config.module.rules).not.toContain(EXISTING_LESS_RULES); + expect(config.module.rules[0]?.sideEffects).toBeTruthy(); + expect(config.module.rules[0]?.use).toMatchInlineSnapshot(` + [ + { + "loader": "path/to/project/node_modules/style-loader/dist/cjs.js", + }, + { + "loader": "path/to/project/node_modules/css-loader/dist/cjs.js", + "options": { + "importLoaders": 2, + "modules": { + "auto": true, + "namedExport": true, + }, + }, + }, + { + "loader": "path/to/project/node_modules/postcss-loader/dist/cjs.js", + "options": { + "implementation": "require.resolve(\\"postcss\\")", + }, + }, + { + "loader": "path/to/project/node_modules/less-loader/dist/cjs.js", + "options": { + "implementation": "require.resolve(\\"less\\")", + "sourceMap": true, + }, + }, + ] + `); + }); + + it("OVERRIDE RULE: it should replace existing scss rules with the given rule", async ({ + expect, + }) => { + const RULE_OVERRIDE: RuleSetRule = { + test: LESS_FILE_REGEX, + use: ["custom-loader", { loader: "css-loader", options: {} }], + }; + + const config = createTestWebpackConfig("override"); + + patchOrAddLessRule(config, { + lessBuildRule: RULE_OVERRIDE, + }); + + expect(config.module.rules.length).toEqual(1); + expect(config.module.rules).toContain(RULE_OVERRIDE); + }); + }); +}); diff --git a/src/webpack/less/webpack.ts b/src/webpack/less/webpack.ts index b30dac3..620f815 100644 --- a/src/webpack/less/webpack.ts +++ b/src/webpack/less/webpack.ts @@ -1,18 +1,31 @@ import type { RuleSetRule, Configuration as WebpackConfig } from "webpack"; import type { AddonStylingOptions } from "../../types"; -const isRuleForLESS = (rule: RuleSetRule) => +export const isRuleForLESS = (rule: RuleSetRule) => typeof rule !== "string" && rule.test instanceof RegExp && rule.test.test("test.less"); -const buildStyleLoader = (options: AddonStylingOptions) => ({ +export const buildStyleLoader = (options: AddonStylingOptions) => ({ loader: require.resolve("style-loader"), }); -const buildCssLoader = ({ cssModules, postCss }: AddonStylingOptions) => { +export const buildCssModuleRules = ({ cssModules }: AddonStylingOptions) => { + if (!cssModules) { + return {}; + } + + return typeof cssModules === "object" + ? { modules: { ...cssModules } } + : { modules: { auto: true } }; +}; + +export const buildCssLoader = ({ + cssModules, + postCss, +}: AddonStylingOptions) => { const importSettings = { importLoaders: postCss ? 2 : 1 }; - const moduleSettings = cssModules ? { modules: { auto: true } } : {}; + const moduleSettings = buildCssModuleRules({ cssModules }); return { loader: require.resolve("css-loader"), @@ -23,7 +36,7 @@ const buildCssLoader = ({ cssModules, postCss }: AddonStylingOptions) => { }; }; -const buildPostCssLoader = ({ postCss }: AddonStylingOptions) => { +export const buildPostCssLoader = ({ postCss }: AddonStylingOptions) => { const implementationOptions = typeof postCss === "object" ? { ...postCss } : {}; @@ -35,7 +48,7 @@ const buildPostCssLoader = ({ postCss }: AddonStylingOptions) => { }; }; -const buildLessLoader = ({ less }: AddonStylingOptions) => { +export const buildLessLoader = ({ less }: AddonStylingOptions) => { const implementationOptions = typeof less === "object" && less.hasOwnProperty("implementation") ? { implementation: less.implementation } @@ -46,20 +59,25 @@ const buildLessLoader = ({ less }: AddonStylingOptions) => { ? { additionalData: less.additionalData } : {}; + const lessOptions = + typeof less === "object" && less.hasOwnProperty("lessOptions") + ? { lessOptions: less.lessOptions } + : {}; + return { loader: require.resolve("less-loader"), options: { sourceMap: true, - lessOptions: less.lessOptions ?? {}, ...implementationOptions, ...additionalData, + ...lessOptions, }, }; }; -const LESS_FILE_REGEX = /\.less$/i; +export const LESS_FILE_REGEX = /\.less$/i; const buildLessRule = (options: AddonStylingOptions): RuleSetRule => { - if (options.scssBuildRule) return options.scssBuildRule; + if (options.lessBuildRule) return options.lessBuildRule; const buildRule = [ buildStyleLoader(options), diff --git a/src/webpack/scss/webpack.test.ts b/src/webpack/scss/webpack.test.ts new file mode 100644 index 0000000..a04f853 --- /dev/null +++ b/src/webpack/scss/webpack.test.ts @@ -0,0 +1,378 @@ +import { describe, it } from "vitest"; +import { + isRuleForSCSS, + buildStyleLoader, + buildCssModuleRules, + buildCssLoader, + buildPostCssLoader, + buildUrlResolverLoader, + buildSassLoader, + patchOrAddScssRule, + SCSS_FILE_REGEX, +} from "./webpack"; + +import type { RuleSetRule, Configuration as WebpackConfig } from "webpack"; + +describe("WEBPACK/SCSS: configuration builders for scss files", () => { + describe("UTILITY: isRuleForSCSS", () => { + it("TRUE: it should return true when given a webpack rule for scss files", async ({ + expect, + }) => { + const webpackRule: RuleSetRule = { + test: /\.scss$/i, + use: [], + }; + const result = isRuleForSCSS(webpackRule); + + expect(result).toBeTruthy(); + }); + + it("FALSE: it should return false when given a webpack rule for other file types", async ({ + expect, + }) => { + const someWebpackRule: RuleSetRule = { + test: /\.svg$/i, + use: [], + }; + const someResult = isRuleForSCSS(someWebpackRule); + + const otherWebpackRule: RuleSetRule = { + test: /\.svg$/i, + use: [], + }; + const otherResult = isRuleForSCSS(otherWebpackRule); + + expect(someResult).toBeFalsy(); + expect(otherResult).toBeFalsy(); + }); + }); + + describe("UTILITY: buildStyleLoader", () => { + it("LOADER: it should return a style-loader object", async ({ expect }) => { + const result = buildStyleLoader({}); + + expect(result).toHaveProperty("loader"); + expect(result.loader).toEqual(require.resolve("style-loader")); + }); + }); + + describe("UTILITY: buildCssModuleRules", () => { + it("NO MODULE RULES: it should return an empty object when no module rules are given", async ({ + expect, + }) => { + const result = buildCssModuleRules({}); + + expect(result).toMatchObject({}); + }); + + it('BASIC: it should return a minimal config for css modules when given "{ cssModules: true }"', async ({ + expect, + }) => { + const result = buildCssModuleRules({ cssModules: true }); + + expect(result).toMatchObject({ modules: { auto: true } }); + }); + + it("CUSTOM: it should return a custom config for css modules when given custom rules", async ({ + expect, + }) => { + const customRules = { + auto: true, + namedExport: true, + getLocalIdent: (context, localIdentName, localName, options) => { + return "whatever_random_class_name"; + }, + }; + + const result = buildCssModuleRules({ cssModules: { ...customRules } }); + + expect(result).toMatchObject({ modules: customRules }); + }); + }); + + describe("UTILITY: buildCssLoader", () => { + const BASE_LOADER = { + loader: require.resolve("css-loader"), + options: {}, + }; + + it("NO ADDON OPTIONS: it should return a css-loader with no options", async ({ + expect, + }) => { + const result = buildCssLoader({}); + + expect(result).toMatchObject(BASE_LOADER); + }); + + it("POSTCSS ENABLED: it should configure the css-loader to handle imports AFTER postcss", async ({ + expect, + }) => { + const expected = { + ...BASE_LOADER, + options: { importLoaders: 3 }, + }; + + const result = buildCssLoader({ + postCss: { implementation: 'require.resolve("postcss")' }, + }); + + expect(result).toMatchObject(expected); + }); + + it("CSS MODULES ENABLED: it should configure the css-loader to support css-modules", async ({ + expect, + }) => { + const expected = { + ...BASE_LOADER, + options: { modules: { auto: true } }, + }; + + const result = buildCssLoader({ + cssModules: true, + }); + + expect(result).toMatchObject(expected); + }); + + it("CSS MODULES & POSTCSS ENABLED: it should configure the css-loader to support css-modules and postcss", async ({ + expect, + }) => { + const expected = { + ...BASE_LOADER, + options: { modules: { auto: true }, importLoaders: 3 }, + }; + + const result = buildCssLoader({ + cssModules: true, + postCss: { implementation: 'require.resolve("postcss")' }, + }); + + expect(result).toMatchObject(expected); + }); + }); + + describe("UTILITY: buildPostCssLoader", () => { + it("it should return a postcss-loader object", async ({ expect }) => { + const result = buildPostCssLoader({}); + + expect(result).toHaveProperty("loader"); + expect(result.loader).toEqual(require.resolve("postcss-loader")); + }); + + it("IMPLEMENTATION: it should return a postcss-loader with a given implementation of postcss", async ({ + expect, + }) => { + const result = buildPostCssLoader({ + postCss: { implementation: 'require.resolve("postcss")' }, + }); + + expect(result).toHaveProperty("loader"); + expect(result.loader).toEqual(require.resolve("postcss-loader")); + }); + }); + + describe("UTILITY: buildUrlResolverLoader", () => { + it("LOADER: it should return a resolve-url-loader object", async ({ + expect, + }) => { + const result = buildUrlResolverLoader({}); + + expect(result).toHaveProperty("loader"); + expect(result.loader).toEqual(require.resolve("resolve-url-loader")); + }); + }); + + describe("UTILITY: buildSassLoader", () => { + it("LOADER: it should return a sass-loader object", ({ expect }) => { + const result = buildSassLoader({ + sass: { implementation: 'require.resolve("sass")' }, + }); + + expect(result).toHaveProperty("loader"); + expect(result.loader).toEqual(require.resolve("sass-loader")); + }); + }); + + describe("UTILITY: patchOrAddScssRule", () => { + const EXISTING_SCSS_RULES = { + test: SCSS_FILE_REGEX, + use: ["style-loader", "css-loader"], + }; + + const createTestWebpackConfig = (id: string): WebpackConfig => ({ + name: `testConfig-${id}`, + module: { + rules: [EXISTING_SCSS_RULES], + }, + }); + + it("NO CONFIGURATION: it should leave existing scss rules alone if no configuration is given", async ({ + expect, + }) => { + const config = createTestWebpackConfig("default"); + + patchOrAddScssRule(config, {}); + + expect(config.module.rules).toContainEqual(EXISTING_SCSS_RULES); + }); + + it("ENABLE POSTCSS: it should replace existing scss rules to enable postcss", async ({ + expect, + }) => { + const config = createTestWebpackConfig("postcss"); + + patchOrAddScssRule(config, { + postCss: { implementation: 'require.resolve("postcss")' }, + sass: { implementation: 'require.resolve("sass")' }, + }); + + expect(config.module.rules.length).toEqual(1); + expect(config.module.rules).not.toContain(EXISTING_SCSS_RULES); + expect(config.module.rules[0]?.use).toMatchInlineSnapshot(` + [ + { + "loader": "path/to/project/node_modules/style-loader/dist/cjs.js", + }, + { + "loader": "path/to/project/node_modules/css-loader/dist/cjs.js", + "options": { + "importLoaders": 3, + }, + }, + { + "loader": "path/to/project/node_modules/postcss-loader/dist/cjs.js", + "options": { + "implementation": "require.resolve(\\"postcss\\")", + }, + }, + { + "loader": "path/to/project/node_modules/resolve-url-loader/index.js", + }, + { + "loader": "path/to/project/node_modules/sass-loader/dist/cjs.js", + "options": { + "implementation": "require.resolve(\\"sass\\")", + "sassOptions": { + "implementation": "require.resolve(\\"sass\\")", + }, + "sourceMap": true, + }, + }, + ] + `); + }); + + it("ENABLE CSS MODULES: it should replace existing scss rules to enable css modules", async ({ + expect, + }) => { + const config = createTestWebpackConfig("postcss"); + + patchOrAddScssRule(config, { + cssModules: true, + sass: { implementation: 'require.resolve("sass")' }, + }); + + expect(config.module.rules.length).toEqual(1); + expect(config.module.rules).not.toContain(EXISTING_SCSS_RULES); + expect(config.module.rules[0]?.use).toMatchInlineSnapshot(` + [ + { + "loader": "path/to/project/node_modules/style-loader/dist/cjs.js", + }, + { + "loader": "path/to/project/node_modules/css-loader/dist/cjs.js", + "options": { + "importLoaders": 2, + "modules": { + "auto": true, + }, + }, + }, + { + "loader": "path/to/project/node_modules/resolve-url-loader/index.js", + }, + { + "loader": "path/to/project/node_modules/sass-loader/dist/cjs.js", + "options": { + "implementation": "require.resolve(\\"sass\\")", + "sassOptions": { + "implementation": "require.resolve(\\"sass\\")", + }, + "sourceMap": true, + }, + }, + ] + `); + }); + + it("ENABLE POSTCSS & CSS MODULES: it should replace existing scss rules to enable postcss & css modules", async ({ + expect, + }) => { + const config = createTestWebpackConfig("complex"); + + patchOrAddScssRule(config, { + postCss: { implementation: 'require.resolve("postcss")' }, + sass: { implementation: 'require.resolve("sass")' }, + cssModules: { auto: true, namedExport: true }, + }); + + expect(config.module.rules.length).toEqual(1); + expect(config.module.rules).not.toContain(EXISTING_SCSS_RULES); + expect(config.module.rules[0]?.sideEffects).toBeTruthy(); + expect(config.module.rules[0]?.use).toMatchInlineSnapshot(` + [ + { + "loader": "path/to/project/node_modules/style-loader/dist/cjs.js", + }, + { + "loader": "path/to/project/node_modules/css-loader/dist/cjs.js", + "options": { + "importLoaders": 3, + "modules": { + "auto": true, + "namedExport": true, + }, + }, + }, + { + "loader": "path/to/project/node_modules/postcss-loader/dist/cjs.js", + "options": { + "implementation": "require.resolve(\\"postcss\\")", + }, + }, + { + "loader": "path/to/project/node_modules/resolve-url-loader/index.js", + }, + { + "loader": "path/to/project/node_modules/sass-loader/dist/cjs.js", + "options": { + "implementation": "require.resolve(\\"sass\\")", + "sassOptions": { + "implementation": "require.resolve(\\"sass\\")", + }, + "sourceMap": true, + }, + }, + ] + `); + }); + + it("OVERRIDE RULE: it should replace existing scss rules with the given rule", async ({ + expect, + }) => { + const RULE_OVERRIDE: RuleSetRule = { + test: SCSS_FILE_REGEX, + use: ["custom-loader", { loader: "css-loader", options: {} }], + }; + + const config = createTestWebpackConfig("override"); + + patchOrAddScssRule(config, { + scssBuildRule: RULE_OVERRIDE, + }); + + expect(config.module.rules.length).toEqual(1); + expect(config.module.rules).toContain(RULE_OVERRIDE); + }); + }); +}); diff --git a/src/webpack/scss/webpack.ts b/src/webpack/scss/webpack.ts index 419908c..be0dc88 100644 --- a/src/webpack/scss/webpack.ts +++ b/src/webpack/scss/webpack.ts @@ -1,18 +1,31 @@ import type { RuleSetRule, Configuration as WebpackConfig } from "webpack"; import type { AddonStylingOptions } from "../../types"; -const isRuleForSCSS = (rule: RuleSetRule) => +export const isRuleForSCSS = (rule: RuleSetRule) => typeof rule !== "string" && rule.test instanceof RegExp && (rule.test.test("test.scss") || rule.test.test("test.sass")); -const buildStyleLoader = (options: AddonStylingOptions) => ({ +export const buildStyleLoader = (options: AddonStylingOptions) => ({ loader: require.resolve("style-loader"), }); -const buildCssLoader = ({ cssModules, postCss }: AddonStylingOptions) => { +export const buildCssModuleRules = ({ cssModules }: AddonStylingOptions) => { + if (!cssModules) { + return {}; + } + + return typeof cssModules === "object" + ? { modules: { ...cssModules } } + : { modules: { auto: true } }; +}; + +export const buildCssLoader = ({ + cssModules, + postCss, +}: AddonStylingOptions) => { const importSettings = { importLoaders: postCss ? 3 : 2 }; - const moduleSettings = cssModules ? { modules: { auto: true } } : {}; + const moduleSettings = buildCssModuleRules({ cssModules }); return { loader: require.resolve("css-loader"), @@ -23,7 +36,7 @@ const buildCssLoader = ({ cssModules, postCss }: AddonStylingOptions) => { }; }; -const buildPostCssLoader = ({ postCss }: AddonStylingOptions) => { +export const buildPostCssLoader = ({ postCss }: AddonStylingOptions) => { const implementationOptions = typeof postCss === "object" ? { ...postCss } : {}; @@ -35,11 +48,11 @@ const buildPostCssLoader = ({ postCss }: AddonStylingOptions) => { }; }; -const buildUrlResolverLoader = (options: AddonStylingOptions) => ({ +export const buildUrlResolverLoader = (options: AddonStylingOptions) => ({ loader: require.resolve("resolve-url-loader"), }); -const buildSassLoader = ({ sass }: AddonStylingOptions) => { +export const buildSassLoader = ({ sass }: AddonStylingOptions) => { const sassOptions = typeof sass === "object" ? { sassOptions: sass } : {}; const implementationOptions = @@ -67,7 +80,7 @@ const buildSassLoader = ({ sass }: AddonStylingOptions) => { }; }; -const SCSS_FILE_REGEX = /\.s[ac]ss$/; +export const SCSS_FILE_REGEX = /\.s[ac]ss$/; const buildScssRule = (options: AddonStylingOptions): RuleSetRule => { if (options.scssBuildRule) return options.scssBuildRule; diff --git a/vite.config.js b/vite.config.ts similarity index 99% rename from vite.config.js rename to vite.config.ts index 9cc50ea..f0b2deb 100644 --- a/vite.config.js +++ b/vite.config.ts @@ -1,4 +1,5 @@ import { defineConfig } from "vite"; + import react from "@vitejs/plugin-react"; // https://vitejs.dev/config/ diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..9a66d42 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,13 @@ +import { defineConfig, mergeConfig } from "vitest/config"; +import viteConfig from "./vite.config"; + +export default mergeConfig( + viteConfig, + defineConfig({ + test: { + passWithNoTests: true, + reporters: "verbose", + setupFiles: ["./vitest/setup.ts"], + }, + }) +); diff --git a/vitest/setup.ts b/vitest/setup.ts new file mode 100644 index 0000000..2d15609 --- /dev/null +++ b/vitest/setup.ts @@ -0,0 +1,24 @@ +import path from "path"; +import { expect } from "vitest"; + +const pathToRemove = path.resolve(__dirname, "../"); + +const redactProjectPathInUse = { + serialize(val, config, indentation, depth, refs, printer) { + const valAsString = JSON.stringify(val, null, 2); + + const sanitizedVal = valAsString.replaceAll( + pathToRemove, + "path/to/project" + ); + + return printer(JSON.parse(sanitizedVal), config, indentation, depth, refs); + }, + test(val) { + return ( + Array.isArray(val) && JSON.stringify(val, null, 2).includes(pathToRemove) + ); + }, +}; + +expect.addSnapshotSerializer(redactProjectPathInUse); diff --git a/yarn.lock b/yarn.lock index be103f7..8177619 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3343,6 +3343,18 @@ "@types/connect" "*" "@types/node" "*" +"@types/chai-subset@^1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@types/chai-subset/-/chai-subset-1.3.3.tgz#97893814e92abd2c534de422cb377e0e0bdaac94" + integrity sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw== + dependencies: + "@types/chai" "*" + +"@types/chai@*", "@types/chai@^4.3.4": + version "4.3.5" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.5.tgz#ae69bcbb1bebb68c4ac0b11e9d8ed04526b3562b" + integrity sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng== + "@types/command-line-args@^5.0.0": version "5.0.0" resolved "https://registry.yarnpkg.com/@types/command-line-args/-/command-line-args-5.0.0.tgz#484e704d20dbb8754a8f091eee45cdd22bcff28c" @@ -3638,6 +3650,50 @@ magic-string "^0.27.0" react-refresh "^0.14.0" +"@vitest/expect@0.31.0": + version "0.31.0" + resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-0.31.0.tgz#37ab35d4f75c12826c204f2a0290e0c2e5ef1192" + integrity sha512-Jlm8ZTyp6vMY9iz9Ny9a0BHnCG4fqBa8neCF6Pk/c/6vkUk49Ls6UBlgGAU82QnzzoaUs9E/mUhq/eq9uMOv/g== + dependencies: + "@vitest/spy" "0.31.0" + "@vitest/utils" "0.31.0" + chai "^4.3.7" + +"@vitest/runner@0.31.0": + version "0.31.0" + resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-0.31.0.tgz#ca830405ae4c2744ae5fb7fbe85df81b56430ebc" + integrity sha512-H1OE+Ly7JFeBwnpHTrKyCNm/oZgr+16N4qIlzzqSG/YRQDATBYmJb/KUn3GrZaiQQyL7GwpNHVZxSQd6juLCgw== + dependencies: + "@vitest/utils" "0.31.0" + concordance "^5.0.4" + p-limit "^4.0.0" + pathe "^1.1.0" + +"@vitest/snapshot@0.31.0": + version "0.31.0" + resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-0.31.0.tgz#f59c4bcf0d03f1f494ee09286965e60a1e0cab64" + integrity sha512-5dTXhbHnyUMTMOujZPB0wjFjQ6q5x9c8TvAsSPUNKjp1tVU7i9pbqcKPqntyu2oXtmVxKbuHCqrOd+Ft60r4tg== + dependencies: + magic-string "^0.30.0" + pathe "^1.1.0" + pretty-format "^27.5.1" + +"@vitest/spy@0.31.0": + version "0.31.0" + resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-0.31.0.tgz#98cb19046c0bd2673a73d6c90ee1533d1be82136" + integrity sha512-IzCEQ85RN26GqjQNkYahgVLLkULOxOm5H/t364LG0JYb3Apg0PsYCHLBYGA006+SVRMWhQvHlBBCyuByAMFmkg== + dependencies: + tinyspy "^2.1.0" + +"@vitest/utils@0.31.0": + version "0.31.0" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-0.31.0.tgz#d0aae17150b95ebf7afdf4e5db8952ac21610ffa" + integrity sha512-kahaRyLX7GS1urekRXN2752X4gIgOGVX4Wo8eDUGUkTWlGpXzf5ZS6N9RUUS+Re3XEE8nVGqNyxkSxF5HXlGhQ== + dependencies: + concordance "^5.0.4" + loupe "^2.3.6" + pretty-format "^27.5.1" + "@webassemblyjs/ast@1.11.1": version "1.11.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" @@ -3799,12 +3855,17 @@ acorn-walk@^7.2.0: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== +acorn-walk@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + acorn@^7.4.1: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.0: +acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.0, acorn@^8.8.2: version "8.8.2" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== @@ -3939,6 +4000,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + any-promise@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" @@ -4065,6 +4131,11 @@ assert@^2.0.0: object-is "^1.0.1" util "^0.12.0" +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== + assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" @@ -4306,6 +4377,11 @@ bl@^4.0.3: inherits "^2.0.4" readable-stream "^3.4.0" +blueimp-md5@^2.10.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.19.0.tgz#b53feea5498dcb53dc6ec4b823adb84b729c4af0" + integrity sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w== + body-parser@1.20.1: version "1.20.1" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" @@ -4500,7 +4576,7 @@ c8@^7.6.0: yargs "^16.2.0" yargs-parser "^20.2.9" -cac@^6.7.12: +cac@^6.7.12, cac@^6.7.14: version "6.7.14" resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== @@ -4566,6 +4642,19 @@ case-sensitive-paths-webpack-plugin@^2.4.0: resolved "https://registry.yarnpkg.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz#db64066c6422eed2e08cc14b986ca43796dbc6d4" integrity sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw== +chai@^4.3.7: + version "4.3.7" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.7.tgz#ec63f6df01829088e8bf55fca839bcd464a8ec51" + integrity sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.2" + deep-eql "^4.1.2" + get-func-name "^2.0.0" + loupe "^2.3.1" + pathval "^1.1.1" + type-detect "^4.0.5" + chalk@^2.0.0, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -4591,6 +4680,11 @@ chalk@^4.0.2, chalk@^4.1.2: ansi-styles "^4.1.0" supports-color "^7.1.0" +check-error@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" + integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA== + chokidar@^3.4.0: version "3.5.1" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" @@ -4848,6 +4942,20 @@ concat-stream@^1.6.2: readable-stream "^2.2.2" typedarray "^0.0.6" +concordance@^5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/concordance/-/concordance-5.0.4.tgz#9896073261adced72f88d60e4d56f8efc4bbbbd2" + integrity sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw== + dependencies: + date-time "^3.1.0" + esutils "^2.0.3" + fast-diff "^1.2.0" + js-string-escape "^1.0.1" + lodash "^4.17.15" + md5-hex "^3.0.1" + semver "^7.3.2" + well-known-symbols "^2.0.0" + concurrently@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-6.2.0.tgz#587e2cb8afca7234172d8ea55176088632c4c56d" @@ -5033,6 +5141,13 @@ date-fns@^2.16.1: resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.22.1.tgz#1e5af959831ebb1d82992bf67b765052d8f0efc4" integrity sha512-yUFPQjrxEmIsMqlHhAhmxkuH769baF21Kk+nZwZGyrMoyLA+LugaQtC0+Tqf9CBUUULWwUJt6Q5ySI3LJDDCGg== +date-time@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/date-time/-/date-time-3.1.0.tgz#0d1e934d170579f481ed8df1e2b8ff70ee845e1e" + integrity sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg== + dependencies: + time-zone "^1.0.0" + debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -5064,6 +5179,13 @@ dedent@^0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= +deep-eql@^4.1.2: + version "4.1.3" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d" + integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw== + dependencies: + type-detect "^4.0.0" + deep-extend@^0.6.0, deep-extend@~0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" @@ -5515,7 +5637,7 @@ estree-walker@^2.0.1: resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== -esutils@^2.0.2: +esutils@^2.0.2, esutils@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== @@ -5659,6 +5781,11 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-diff@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + fast-glob@^3.1.1: version "3.2.5" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.5.tgz#7939af2a656de79a4f1901903ee8adcaa7cb9661" @@ -5998,6 +6125,11 @@ get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-func-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" + integrity sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig== + get-intrinsic@^1.0.2: version "1.1.1" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" @@ -6884,6 +7016,11 @@ joycon@^3.0.1: resolved "https://registry.yarnpkg.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03" integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw== +js-string-escape@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef" + integrity sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -6978,6 +7115,11 @@ json5@^2.2.2: resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== +jsonc-parser@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" + integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== + jsonfile@^6.0.1: version "6.1.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" @@ -7098,6 +7240,11 @@ loader-utils@^2.0.0: emojis-list "^3.0.0" json5 "^2.1.2" +local-pkg@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/local-pkg/-/local-pkg-0.4.3.tgz#0ff361ab3ae7f1c19113d9bb97b98b905dbc4963" + integrity sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g== + locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" @@ -7153,7 +7300,7 @@ lodash.sortby@^4.7.0: resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA== -lodash@^4.17.20, lodash@^4.17.21: +lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -7173,6 +7320,13 @@ loose-envify@^1.1.0, loose-envify@^1.4.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" +loupe@^2.3.1, loupe@^2.3.6: + version "2.3.6" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.6.tgz#76e4af498103c532d1ecc9be102036a21f787b53" + integrity sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA== + dependencies: + get-func-name "^2.0.0" + lower-case@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" @@ -7201,6 +7355,13 @@ magic-string@^0.27.0: dependencies: "@jridgewell/sourcemap-codec" "^1.4.13" +magic-string@^0.30.0: + version "0.30.0" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.0.tgz#fd58a4748c5c4547338a424e90fa5dd17f4de529" + integrity sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ== + dependencies: + "@jridgewell/sourcemap-codec" "^1.4.13" + make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" @@ -7250,6 +7411,13 @@ markdown-to-jsx@^7.1.8: resolved "https://registry.yarnpkg.com/markdown-to-jsx/-/markdown-to-jsx-7.1.9.tgz#1ffae0cda07c189163d273bd57a5b8f8f8745586" integrity sha512-x4STVIKIJR0mGgZIZ5RyAeQD7FEZd5tS8m/htbcVGlex32J+hlSLj+ExrHCxP6nRKF1EKbcO7i6WhC1GtOpBlA== +md5-hex@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/md5-hex/-/md5-hex-3.0.1.tgz#be3741b510591434b2784d79e556eefc2c9a8e5c" + integrity sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw== + dependencies: + blueimp-md5 "^2.10.0" + mdast-util-definitions@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz#c5c1a84db799173b4dcf7643cda999e440c24db2" @@ -7461,6 +7629,16 @@ mkdirp@^1.0.3: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== +mlly@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.2.0.tgz#f0f6c2fc8d2d12ea6907cd869066689b5031b613" + integrity sha512-+c7A3CV0KGdKcylsI6khWyts/CYrGTrRVo4R/I7u/cUsy0Conxa6LUhiEzVKIw14lc2L5aiO4+SeVe4TeGRKww== + dependencies: + acorn "^8.8.2" + pathe "^1.1.0" + pkg-types "^1.0.2" + ufo "^1.1.1" + module-alias@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/module-alias/-/module-alias-2.2.2.tgz#151cdcecc24e25739ff0aa6e51e1c5716974c0e0" @@ -7505,6 +7683,11 @@ nanoid@^3.3.1, nanoid@^3.3.4: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== +nanoid@^3.3.6: + version "3.3.6" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" + integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -7777,6 +7960,13 @@ p-limit@^3.0.2: dependencies: yocto-queue "^0.1.0" +p-limit@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-4.0.0.tgz#914af6544ed32bfa54670b061cafcbd04984b644" + integrity sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ== + dependencies: + yocto-queue "^1.0.0" + p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" @@ -7952,6 +8142,11 @@ pathe@^1.1.0: resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.0.tgz#e2e13f6c62b31a3289af4ba19886c230f295ec03" integrity sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w== +pathval@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" + integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== + peek-stream@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/peek-stream/-/peek-stream-1.1.3.tgz#3b35d84b7ccbbd262fff31dc10da56856ead6d67" @@ -8025,6 +8220,15 @@ pkg-dir@^5.0.0: dependencies: find-up "^5.0.0" +pkg-types@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.0.3.tgz#988b42ab19254c01614d13f4f65a2cfc7880f868" + integrity sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A== + dependencies: + jsonc-parser "^3.2.0" + mlly "^1.2.0" + pathe "^1.1.0" + polished@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/polished/-/polished-4.2.2.tgz#2529bb7c3198945373c52e34618c8fe7b1aa84d1" @@ -8105,6 +8309,15 @@ postcss@^8.2.14, postcss@^8.4.19, postcss@^8.4.21: picocolors "^1.0.0" source-map-js "^1.0.2" +postcss@^8.4.23: + version "8.4.23" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.23.tgz#df0aee9ac7c5e53e1075c24a3613496f9e6552ab" + integrity sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA== + dependencies: + nanoid "^3.3.6" + picocolors "^1.0.0" + source-map-js "^1.0.2" + prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" @@ -8128,6 +8341,15 @@ pretty-error@^4.0.0: lodash "^4.17.20" renderkid "^3.0.0" +pretty-format@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" + integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== + dependencies: + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" + pretty-hrtime@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" @@ -8346,6 +8568,11 @@ react-is@^16.8.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== +react-is@^17.0.1: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + react-refresh@^0.14.0: version "0.14.0" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e" @@ -8742,6 +8969,13 @@ rimraf@~2.6.2: optionalDependencies: fsevents "~2.3.2" +rollup@^3.21.0: + version "3.21.6" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.21.6.tgz#f5649ccdf8fcc7729254faa457cbea9547eb86db" + integrity sha512-SXIICxvxQxR3D4dp/3LDHZIJPC8a4anKMHd4E3Jiz2/JnY+2bEjqrOokAauc5ShGVNFHlEFjBXAXlaxkJqIqSg== + optionalDependencies: + fsevents "~2.3.2" + run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" @@ -8849,6 +9083,13 @@ semver@^7.0.0: dependencies: lru-cache "^6.0.0" +semver@^7.3.2: + version "7.5.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.0.tgz#ed8c5dc8efb6c629c88b23d41dc9bf40c1d96cd0" + integrity sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA== + dependencies: + lru-cache "^6.0.0" + semver@^7.3.5, semver@^7.3.7, semver@^7.3.8: version "7.3.8" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" @@ -8965,6 +9206,11 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" +siginfo@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30" + integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== + signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" @@ -9143,6 +9389,11 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= +stackback@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b" + integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== + static-extend@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" @@ -9156,6 +9407,11 @@ statuses@2.0.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== +std-env@^3.3.2: + version "3.3.3" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.3.3.tgz#a54f06eb245fdcfef53d56f3c0251f1d5c3d01fe" + integrity sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg== + store2@^2.14.2: version "2.14.2" resolved "https://registry.yarnpkg.com/store2/-/store2-2.14.2.tgz#56138d200f9fe5f582ad63bc2704dbc0e4a45068" @@ -9277,6 +9533,13 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= +strip-literal@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/strip-literal/-/strip-literal-1.0.1.tgz#0115a332710c849b4e46497891fb8d585e404bd2" + integrity sha512-QZTsipNpa2Ppr6v1AmJHESqJ3Uz247MUS0OjrnnZjFAvEoWqxuyFuXn2xLgMtRnijJShAa1HL0gtJyUs7u7n3Q== + dependencies: + acorn "^8.8.2" + style-loader@^3.3.1, style-loader@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.2.tgz#eaebca714d9e462c19aa1e3599057bc363924899" @@ -9476,11 +9739,31 @@ through2@^2.0.3: readable-stream "~2.3.6" xtend "~4.0.1" +time-zone@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/time-zone/-/time-zone-1.0.0.tgz#99c5bf55958966af6d06d83bdf3800dc82faec5d" + integrity sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA== + +tinybench@^2.4.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.5.0.tgz#4711c99bbf6f3e986f67eb722fed9cddb3a68ba5" + integrity sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA== + tinycolor2@^1.4.1: version "1.4.2" resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.2.tgz#3f6a4d1071ad07676d7fa472e1fac40a719d8803" integrity sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA== +tinypool@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-0.5.0.tgz#3861c3069bf71e4f1f5aa2d2e6b3aaacc278961e" + integrity sha512-paHQtnrlS1QZYKF/GnLoOM/DN9fqaGOFbCbxzAhwniySnzl9Ebk8w73/dd34DAhe/obUbPAOldTyYXQZxnPBPQ== + +tinyspy@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-2.1.0.tgz#bd6875098f988728e6456cfd5ab8cc06498ecdeb" + integrity sha512-7eORpyqImoOvkQJCSkL0d0mB4NHHIFAy4b1u8PHdDa7SjGS2njzl6/lyGoZLm+eyYEtlUmFGE0rFj66SWxZgQQ== + tmpl@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" @@ -9619,6 +9902,11 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" +type-detect@^4.0.0, type-detect@^4.0.5: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + type-fest@^0.16.0: version "0.16.0" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.16.0.tgz#3240b891a78b0deae910dbeb86553e552a148860" @@ -9682,6 +9970,11 @@ typical@^5.2.0: resolved "https://registry.yarnpkg.com/typical/-/typical-5.2.0.tgz#4daaac4f2b5315460804f0acf6cb69c52bb93066" integrity sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg== +ufo@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.1.2.tgz#d0d9e0fa09dece0c31ffd57bd363f030a35cfe76" + integrity sha512-TrY6DsjTQQgyS3E3dBaOXf0TpPD8u9FVrVYmKVegJuFw51n/YB9XPt+U6ydzFG5ZIN7+DIjPbNmXoBj9esYhgQ== + uglify-js@^3.1.4: version "3.14.3" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.14.3.tgz#c0f25dfea1e8e5323eccf59610be08b6043c15cf" @@ -9917,6 +10210,29 @@ vary@~1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== +vite-node@0.31.0: + version "0.31.0" + resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-0.31.0.tgz#8794a98f21b0cf2394bfd2aaa5fc85d2c42be084" + integrity sha512-8x1x1LNuPvE2vIvkSB7c1mApX5oqlgsxzHQesYF7l5n1gKrEmrClIiZuOFbFDQcjLsmcWSwwmrWrcGWm9Fxc/g== + dependencies: + cac "^6.7.14" + debug "^4.3.4" + mlly "^1.2.0" + pathe "^1.1.0" + picocolors "^1.0.0" + vite "^3.0.0 || ^4.0.0" + +"vite@^3.0.0 || ^4.0.0": + version "4.3.5" + resolved "https://registry.yarnpkg.com/vite/-/vite-4.3.5.tgz#3871fe0f4b582ea7f49a85386ac80e84826367d9" + integrity sha512-0gEnL9wiRFxgz40o/i/eTBwm+NEbpUeTWhzKrZDSdKm6nplj+z4lKz8ANDgildxHm47Vg8EUia0aicKbawUVVA== + dependencies: + esbuild "^0.17.5" + postcss "^8.4.23" + rollup "^3.21.0" + optionalDependencies: + fsevents "~2.3.2" + vite@^4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/vite/-/vite-4.2.1.tgz#6c2eb337b0dfd80a9ded5922163b94949d7fc254" @@ -9929,6 +10245,37 @@ vite@^4.2.1: optionalDependencies: fsevents "~2.3.2" +vitest@^0.31.0: + version "0.31.0" + resolved "https://registry.yarnpkg.com/vitest/-/vitest-0.31.0.tgz#133e98f779aa81afbc7ee1fcb385a0c458b8c2c8" + integrity sha512-JwWJS9p3GU9GxkG7eBSmr4Q4x4bvVBSswaCFf1PBNHiPx00obfhHRJfgHcnI0ffn+NMlIh9QGvG75FlaIBdKGA== + dependencies: + "@types/chai" "^4.3.4" + "@types/chai-subset" "^1.3.3" + "@types/node" "*" + "@vitest/expect" "0.31.0" + "@vitest/runner" "0.31.0" + "@vitest/snapshot" "0.31.0" + "@vitest/spy" "0.31.0" + "@vitest/utils" "0.31.0" + acorn "^8.8.2" + acorn-walk "^8.2.0" + cac "^6.7.14" + chai "^4.3.7" + concordance "^5.0.4" + debug "^4.3.4" + local-pkg "^0.4.3" + magic-string "^0.30.0" + pathe "^1.1.0" + picocolors "^1.0.0" + std-env "^3.3.2" + strip-literal "^1.0.1" + tinybench "^2.4.0" + tinypool "^0.5.0" + vite "^3.0.0 || ^4.0.0" + vite-node "0.31.0" + why-is-node-running "^2.2.2" + walker@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" @@ -10014,6 +10361,11 @@ webpack@5: watchpack "^2.4.0" webpack-sources "^3.2.3" +well-known-symbols@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/well-known-symbols/-/well-known-symbols-2.0.0.tgz#e9c7c07dbd132b7b84212c8174391ec1f9871ba5" + integrity sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q== + whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" @@ -10050,6 +10402,14 @@ which@^2.0.1, which@^2.0.2: dependencies: isexe "^2.0.0" +why-is-node-running@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.2.2.tgz#4185b2b4699117819e7154594271e7e344c9973e" + integrity sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA== + dependencies: + siginfo "^2.0.0" + stackback "0.0.2" + wide-align@^1.1.2: version "1.1.5" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" @@ -10191,6 +10551,11 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== +yocto-queue@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251" + integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== + zx@^1.14.1: version "1.14.1" resolved "https://registry.yarnpkg.com/zx/-/zx-1.14.1.tgz#e1a93090aaa27134371660964090fafedc2ef1e6"