@najemi-software/typed-scss-modules 10.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +437 -0
  3. package/dist/lib/cli.d.ts +2 -0
  4. package/dist/lib/cli.js +123 -0
  5. package/dist/lib/core/alerts.d.ts +14 -0
  6. package/dist/lib/core/alerts.js +25 -0
  7. package/dist/lib/core/generate.d.ts +8 -0
  8. package/dist/lib/core/generate.js +18 -0
  9. package/dist/lib/core/index.d.ts +6 -0
  10. package/dist/lib/core/index.js +5 -0
  11. package/dist/lib/core/list-different.d.ts +3 -0
  12. package/dist/lib/core/list-different.js +42 -0
  13. package/dist/lib/core/list-files-and-perform-sanity-checks.d.ts +9 -0
  14. package/dist/lib/core/list-files-and-perform-sanity-checks.js +22 -0
  15. package/dist/lib/core/remove-file.d.ts +7 -0
  16. package/dist/lib/core/remove-file.js +28 -0
  17. package/dist/lib/core/types.d.ts +21 -0
  18. package/dist/lib/core/types.js +1 -0
  19. package/dist/lib/core/watch.d.ts +8 -0
  20. package/dist/lib/core/watch.js +34 -0
  21. package/dist/lib/core/write-file.d.ts +8 -0
  22. package/dist/lib/core/write-file.js +62 -0
  23. package/dist/lib/implementations/compilers.d.ts +14 -0
  24. package/dist/lib/implementations/compilers.js +49 -0
  25. package/dist/lib/implementations/implementations.d.ts +27 -0
  26. package/dist/lib/implementations/implementations.js +54 -0
  27. package/dist/lib/implementations/index.d.ts +2 -0
  28. package/dist/lib/implementations/index.js +2 -0
  29. package/dist/lib/index.d.ts +3 -0
  30. package/dist/lib/index.js +2 -0
  31. package/dist/lib/load.d.ts +20 -0
  32. package/dist/lib/load.js +87 -0
  33. package/dist/lib/main.d.ts +2 -0
  34. package/dist/lib/main.js +34 -0
  35. package/dist/lib/prettier/can-resolve.d.ts +1 -0
  36. package/dist/lib/prettier/can-resolve.js +12 -0
  37. package/dist/lib/prettier/index.d.ts +8 -0
  38. package/dist/lib/prettier/index.js +44 -0
  39. package/dist/lib/sass/file-to-class-names.d.ts +25 -0
  40. package/dist/lib/sass/file-to-class-names.js +55 -0
  41. package/dist/lib/sass/importer.d.ts +29 -0
  42. package/dist/lib/sass/importer.js +37 -0
  43. package/dist/lib/sass/index.d.ts +1 -0
  44. package/dist/lib/sass/index.js +1 -0
  45. package/dist/lib/sass/source-to-class-names.d.ts +6 -0
  46. package/dist/lib/sass/source-to-class-names.js +16 -0
  47. package/dist/lib/typescript/class-names-to-type-definition.d.ts +20 -0
  48. package/dist/lib/typescript/class-names-to-type-definition.js +64 -0
  49. package/dist/lib/typescript/get-type-definition-path.d.ts +8 -0
  50. package/dist/lib/typescript/get-type-definition-path.js +25 -0
  51. package/dist/lib/typescript/index.d.ts +3 -0
  52. package/dist/lib/typescript/index.js +3 -0
  53. package/package.json +120 -0
@@ -0,0 +1,44 @@
1
+ import { format, resolveConfig } from "prettier";
2
+ import { alerts } from "../core/alerts.js";
3
+ import { canResolvePrettier } from "./can-resolve.js";
4
+ const isPrettier = (t) => !!t &&
5
+ typeof t === "object" &&
6
+ t !== null &&
7
+ "format" in t &&
8
+ typeof t.format === "function" &&
9
+ "resolveConfig" in t &&
10
+ typeof t.resolveConfig === "function";
11
+ /**
12
+ * Try to load prettier and config from project to format input,
13
+ * fall back to input if prettier is not found or failed
14
+ *
15
+ * @param {file} file
16
+ * @param {string} input
17
+ */
18
+ export const attemptPrettier = async (file, input) => {
19
+ if (!canResolvePrettier()) {
20
+ return input;
21
+ }
22
+ const prettier = await import("prettier");
23
+ if (!isPrettier(prettier)) {
24
+ // doesn't look like prettier
25
+ return input;
26
+ }
27
+ try {
28
+ const config = await prettier
29
+ .resolveConfig(file, {
30
+ editorconfig: true,
31
+ })
32
+ .catch(() => {
33
+ /* default */
34
+ });
35
+ // try to return formatted output
36
+ return prettier.format(input, { ...config, parser: "typescript" });
37
+ }
38
+ catch (error) {
39
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
40
+ alerts.notice(`Tried using prettier, but failed with error: ${error}`);
41
+ }
42
+ // failed to format
43
+ return input;
44
+ };
@@ -0,0 +1,25 @@
1
+ import { type Options } from "sass";
2
+ import { type Implementations } from "../implementations/index.js";
3
+ import { type Aliases, type SASSImporter, type SASSImporterOptions, type SyncMode } from "./importer.js";
4
+ export type { Aliases };
5
+ export type ClassName = string;
6
+ declare const transformersMap: {
7
+ readonly camel: (className: ClassName) => string;
8
+ readonly dashes: (className: ClassName) => string;
9
+ readonly kebab: (className: ClassName) => string;
10
+ readonly none: (className: ClassName) => string;
11
+ readonly param: (className: ClassName) => string;
12
+ readonly snake: (className: ClassName) => string;
13
+ };
14
+ type NameFormatWithTransformer = keyof typeof transformersMap;
15
+ export declare const NAME_FORMATS: readonly [...("camel" | "dashes" | "kebab" | "none" | "param" | "snake")[], "all"];
16
+ export type NameFormat = (typeof NAME_FORMATS)[number];
17
+ export type InternalSassOptions = Pick<Options<SyncMode>, "style" | "loadPaths" | "silenceDeprecations">;
18
+ export interface SASSOptions extends SASSImporterOptions, InternalSassOptions {
19
+ nameFormat?: string | string[];
20
+ implementation: Implementations;
21
+ async?: boolean;
22
+ importers?: SASSImporter<SyncMode>[];
23
+ }
24
+ export declare const nameFormatDefault: NameFormatWithTransformer;
25
+ export declare const fileToClassNames: (file: string, { style, loadPaths, silenceDeprecations, nameFormat: rawNameFormat, implementation, aliases, aliasPrefixes, importers, async, }?: SASSOptions) => Promise<string[]>;
@@ -0,0 +1,55 @@
1
+ import { camelCase, camelCaseTransformMerge, paramCase, snakeCase, } from "change-case";
2
+ import {} from "sass";
3
+ import { getAsyncCompiler, getImplementation, getSyncCompiler, } from "../implementations/index.js";
4
+ import { customImporters, } from "./importer.js";
5
+ import { sourceToClassNames } from "./source-to-class-names.js";
6
+ const transformersMap = {
7
+ camel: (className) => camelCase(className, { transform: camelCaseTransformMerge }),
8
+ dashes: (className) => /-/.test(className) ? camelCase(className) : className,
9
+ kebab: (className) => transformersMap.param(className),
10
+ none: (className) => className,
11
+ param: (className) => paramCase(className),
12
+ snake: (className) => snakeCase(className),
13
+ };
14
+ const NAME_FORMATS_WITH_TRANSFORMER = Object.keys(transformersMap);
15
+ export const NAME_FORMATS = [...NAME_FORMATS_WITH_TRANSFORMER, "all"];
16
+ export const nameFormatDefault = "camel";
17
+ export const fileToClassNames = async (file, { style = "expanded", loadPaths = [], silenceDeprecations = [], nameFormat: rawNameFormat, implementation, aliases, aliasPrefixes, importers = [], async = false, } = {}) => {
18
+ const root = await getImplementation(implementation);
19
+ const nameFormat = (typeof rawNameFormat === "string" ? [rawNameFormat] : rawNameFormat);
20
+ const nameFormats = nameFormat
21
+ ? nameFormat.includes("all")
22
+ ? NAME_FORMATS_WITH_TRANSFORMER
23
+ : nameFormat
24
+ : [nameFormatDefault];
25
+ const result = !async
26
+ ? getSyncCompiler({ implementation, root }).compile(file, {
27
+ style,
28
+ silenceDeprecations, // Suppress specified deprecations
29
+ importers: customImporters({
30
+ aliases,
31
+ aliasPrefixes,
32
+ importers,
33
+ }),
34
+ loadPaths: loadPaths,
35
+ })
36
+ : await (await getAsyncCompiler({ implementation, root })).compileAsync(file, {
37
+ style,
38
+ silenceDeprecations, // Suppress specified deprecations
39
+ importers: customImporters({
40
+ aliases,
41
+ aliasPrefixes,
42
+ importers,
43
+ }),
44
+ loadPaths: loadPaths,
45
+ });
46
+ const classNames = await sourceToClassNames(result.css, file);
47
+ const transformers = nameFormats.map((item) => transformersMap[item]);
48
+ const transformedClassNames = new Set([]);
49
+ classNames.forEach((className) => {
50
+ transformers.forEach((transformer) => {
51
+ transformedClassNames.add(transformer(className));
52
+ });
53
+ });
54
+ return Array.from(transformedClassNames).sort((a, b) => a.localeCompare(b));
55
+ };
@@ -0,0 +1,29 @@
1
+ import type { FileImporter, Importer as SassImporter } from "sass";
2
+ export type SyncMode = "sync" | "async";
3
+ type Importer<TSync extends SyncMode = "sync"> = SassImporter<TSync> | FileImporter<TSync>;
4
+ type AsyncImporter = Importer<"async">;
5
+ export type { AsyncImporter as SASSAsyncImporter, Importer as SASSImporter };
6
+ export interface Aliases {
7
+ [index: string]: string;
8
+ }
9
+ interface AliasImporterOptions {
10
+ aliases: Aliases;
11
+ aliasPrefixes: Aliases;
12
+ }
13
+ /**
14
+ * Construct a SASS importer to create aliases for imports.
15
+ */
16
+ export declare const aliasResolver: ({ aliases, aliasPrefixes }: AliasImporterOptions) => (url: string) => string | null;
17
+ export declare const aliasImporter: <TSync extends SyncMode = "sync">({ aliases, aliasPrefixes, }: AliasImporterOptions) => FileImporter<TSync>;
18
+ export interface SASSImporterOptions {
19
+ aliases?: Aliases;
20
+ aliasPrefixes?: Aliases;
21
+ importers?: Importer[];
22
+ }
23
+ /**
24
+ * Construct custom SASS importers based on options.
25
+ *
26
+ * - Given aliases and alias prefix options, add a custom alias importer.
27
+ * - Given custom SASS importer(s), append to the list of importers.
28
+ */
29
+ export declare const customImporters: <TSync extends SyncMode = "sync">({ aliases, aliasPrefixes, importers, }: SASSImporterOptions) => Importer<TSync>[];
@@ -0,0 +1,37 @@
1
+ import { pathToFileURL } from "url";
2
+ /**
3
+ * Construct a SASS importer to create aliases for imports.
4
+ */
5
+ export const aliasResolver = ({ aliases, aliasPrefixes }) => (url) => {
6
+ if (url in aliases) {
7
+ return aliases[url];
8
+ }
9
+ const prefixMatch = Object.keys(aliasPrefixes).find((prefix) => url.startsWith(prefix));
10
+ if (prefixMatch) {
11
+ return aliasPrefixes[prefixMatch] + url.substr(prefixMatch.length);
12
+ }
13
+ return null;
14
+ };
15
+ export const aliasImporter = ({ aliases, aliasPrefixes, }) => {
16
+ const resolveFileUrl = aliasResolver({ aliases, aliasPrefixes });
17
+ return {
18
+ findFileUrl(url) {
19
+ const alias = resolveFileUrl(url);
20
+ if (!alias)
21
+ return null;
22
+ return pathToFileURL(alias);
23
+ },
24
+ };
25
+ };
26
+ /**
27
+ * Construct custom SASS importers based on options.
28
+ *
29
+ * - Given aliases and alias prefix options, add a custom alias importer.
30
+ * - Given custom SASS importer(s), append to the list of importers.
31
+ */
32
+ export const customImporters = ({ aliases = {}, aliasPrefixes = {}, importers = [], }) => {
33
+ const bundled = [
34
+ aliasImporter({ aliases, aliasPrefixes }),
35
+ ];
36
+ return bundled.concat(importers);
37
+ };
@@ -0,0 +1 @@
1
+ export { NAME_FORMATS, fileToClassNames, nameFormatDefault, type Aliases, type SASSOptions, } from "./file-to-class-names.js";
@@ -0,0 +1 @@
1
+ export { NAME_FORMATS, fileToClassNames, nameFormatDefault, } from "./file-to-class-names.js";
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Converts a CSS source string to a list of exports (class names, keyframes, etc.)
3
+ */
4
+ export declare const sourceToClassNames: (source: {
5
+ toString(): string;
6
+ }, file: string) => Promise<string[]>;
@@ -0,0 +1,16 @@
1
+ import postcss from "postcss";
2
+ import PostcssModulesPlugin from "postcss-modules";
3
+ /**
4
+ * Converts a CSS source string to a list of exports (class names, keyframes, etc.)
5
+ */
6
+ export const sourceToClassNames = async (source, file) => {
7
+ let result = {};
8
+ await postcss([
9
+ PostcssModulesPlugin({
10
+ getJSON: (_, json) => {
11
+ result = json;
12
+ },
13
+ }),
14
+ ]).process(source, { from: file });
15
+ return Object.keys(result);
16
+ };
@@ -0,0 +1,20 @@
1
+ import type { ClassName } from "lib/sass/file-to-class-names.js";
2
+ export type ExportType = "named" | "default";
3
+ export declare const EXPORT_TYPES: ExportType[];
4
+ export type QuoteType = "single" | "double";
5
+ export declare const QUOTE_TYPES: QuoteType[];
6
+ export interface TypeDefinitionOptions {
7
+ banner: string;
8
+ classNames: ClassName[];
9
+ file: string;
10
+ exportType: ExportType;
11
+ exportTypeName?: string;
12
+ exportTypeInterface?: string;
13
+ quoteType?: QuoteType;
14
+ }
15
+ export declare const exportTypeDefault: ExportType;
16
+ export declare const exportTypeNameDefault: string;
17
+ export declare const exportTypeInterfaceDefault: string;
18
+ export declare const quoteTypeDefault: QuoteType;
19
+ export declare const bannerTypeDefault: string;
20
+ export declare const classNamesToTypeDefinitions: (options: TypeDefinitionOptions) => Promise<string | null>;
@@ -0,0 +1,64 @@
1
+ import os from "os";
2
+ import reserved from "reserved-words";
3
+ import { alerts } from "../core/alerts.js";
4
+ export const EXPORT_TYPES = ["named", "default"];
5
+ export const QUOTE_TYPES = ["single", "double"];
6
+ export const exportTypeDefault = "named";
7
+ export const exportTypeNameDefault = "ClassNames";
8
+ export const exportTypeInterfaceDefault = "Styles";
9
+ export const quoteTypeDefault = "single";
10
+ export const bannerTypeDefault = "";
11
+ const classNameToNamedTypeDefinition = (className) => `export declare const ${className}: string;`;
12
+ const classNameToType = (className, quoteType) => {
13
+ const quote = quoteType === "single" ? "'" : '"';
14
+ return ` ${quote}${className}${quote}: string;`;
15
+ };
16
+ const isReservedKeyword = (className) => reserved.check(className, "es5", true) ||
17
+ reserved.check(className, "es6", true);
18
+ const isValidName = (className) => {
19
+ if (isReservedKeyword(className)) {
20
+ alerts.warn(`[SKIPPING] '${className}' is a reserved keyword (consider renaming or using --exportType default).`);
21
+ return false;
22
+ }
23
+ else if (/-/.test(className)) {
24
+ alerts.warn(`[SKIPPING] '${className}' contains dashes (consider using 'camelCase' or 'dashes' for --nameFormat or using --exportType default).`);
25
+ return false;
26
+ }
27
+ return true;
28
+ };
29
+ export const classNamesToTypeDefinitions = async (options) => {
30
+ if (options.classNames.length) {
31
+ const lines = [];
32
+ const { exportTypeName: ClassNames = exportTypeNameDefault, exportTypeInterface: Styles = exportTypeInterfaceDefault, } = options;
33
+ switch (options.exportType) {
34
+ case "default":
35
+ if (options.banner)
36
+ lines.push(options.banner);
37
+ lines.push(`export type ${Styles} = {`);
38
+ lines.push(...options.classNames.map((className) => classNameToType(className, options.quoteType || quoteTypeDefault)));
39
+ lines.push(`};${os.EOL}`);
40
+ lines.push(`export type ${ClassNames} = keyof ${Styles};${os.EOL}`);
41
+ lines.push(`declare const styles: ${Styles};${os.EOL}`);
42
+ lines.push(`export default styles;`);
43
+ break;
44
+ case "named":
45
+ if (options.banner)
46
+ lines.push(options.banner);
47
+ lines.push(...options.classNames
48
+ .filter(isValidName)
49
+ .map(classNameToNamedTypeDefinition));
50
+ break;
51
+ }
52
+ if (lines.length) {
53
+ const typeDefinition = lines.join(`${os.EOL}`) + `${os.EOL}`;
54
+ // return await attemptPrettier(options.file, typeDefinition); // ignore prettier (slows down)
55
+ return typeDefinition;
56
+ }
57
+ else {
58
+ return null;
59
+ }
60
+ }
61
+ else {
62
+ return null;
63
+ }
64
+ };
@@ -0,0 +1,8 @@
1
+ import type { ConfigOptions } from "../core/types.js";
2
+ /**
3
+ * Given a file path to a SCSS file, generate the corresponding type definition
4
+ * file path.
5
+ *
6
+ * @param file the SCSS file path
7
+ */
8
+ export declare const getTypeDefinitionPath: (file: string, options: ConfigOptions) => string;
@@ -0,0 +1,25 @@
1
+ import path from "path";
2
+ const CURRENT_WORKING_DIRECTORY = process.cwd();
3
+ /**
4
+ * Given a file path to a SCSS file, generate the corresponding type definition
5
+ * file path.
6
+ *
7
+ * @param file the SCSS file path
8
+ */
9
+ export const getTypeDefinitionPath = (file, options) => {
10
+ let resolvedPath = file;
11
+ if (options.outputFolder) {
12
+ const relativePath = path.relative(CURRENT_WORKING_DIRECTORY, file);
13
+ resolvedPath = path.resolve(CURRENT_WORKING_DIRECTORY, options.outputFolder, relativePath);
14
+ }
15
+ if (options.allowArbitraryExtensions) {
16
+ const resolvedDirname = path.dirname(resolvedPath);
17
+ // Note: `ext` includes a leading period (e.g. '.scss')
18
+ const { name, ext } = path.parse(resolvedPath);
19
+ // @see https://www.typescriptlang.org/tsconfig/#allowArbitraryExtensions
20
+ return path.join(resolvedDirname, `${name}.d${ext}.ts`);
21
+ }
22
+ else {
23
+ return `${resolvedPath}.d.ts`;
24
+ }
25
+ };
@@ -0,0 +1,3 @@
1
+ export { LOG_LEVELS, logLevelDefault, type LogLevel } from "../core/alerts.js";
2
+ export { EXPORT_TYPES, QUOTE_TYPES, bannerTypeDefault, classNamesToTypeDefinitions, exportTypeDefault, exportTypeInterfaceDefault, exportTypeNameDefault, quoteTypeDefault, type ExportType, type QuoteType, } from "./class-names-to-type-definition.js";
3
+ export { getTypeDefinitionPath } from "./get-type-definition-path.js";
@@ -0,0 +1,3 @@
1
+ export { LOG_LEVELS, logLevelDefault } from "../core/alerts.js";
2
+ export { EXPORT_TYPES, QUOTE_TYPES, bannerTypeDefault, classNamesToTypeDefinitions, exportTypeDefault, exportTypeInterfaceDefault, exportTypeNameDefault, quoteTypeDefault, } from "./class-names-to-type-definition.js";
3
+ export { getTypeDefinitionPath } from "./get-type-definition-path.js";
package/package.json ADDED
@@ -0,0 +1,120 @@
1
+ {
2
+ "name": "@najemi-software/typed-scss-modules",
3
+ "version": "10.3.0",
4
+ "private": false,
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "description": "TypeScript type definition generator for SCSS CSS Modules",
9
+ "type": "module",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/lib/index.d.ts",
13
+ "import": "./dist/lib/index.js",
14
+ "require": "./dist/lib/index.js",
15
+ "default": "./dist/lib/index.js"
16
+ }
17
+ },
18
+ "main": "dist/lib/index.js",
19
+ "types": "dist/lib/index.d.ts",
20
+ "author": "am0wa <am0wa.it@gmail.com>",
21
+ "contributors": [
22
+ {
23
+ "name": "Spencer Miskoviak",
24
+ "email": "smiskoviak@gmail.com"
25
+ }
26
+ ],
27
+ "license": "MIT",
28
+ "engines": {
29
+ "node": ">=22.19",
30
+ "npm": ">=10.9"
31
+ },
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "git+https://github.com/Najemi-Software/typed-scss-modules.git"
35
+ },
36
+ "homepage": "https://github.com/Najemi-Software/typed-scss-modules#readme",
37
+ "keywords": [
38
+ "scss",
39
+ "css modules",
40
+ "cli",
41
+ "typescript",
42
+ "type generator",
43
+ "scss modules"
44
+ ],
45
+ "files": [
46
+ "dist/lib"
47
+ ],
48
+ "bin": {
49
+ "typed-scss-modules": "./dist/lib/cli.js"
50
+ },
51
+ "scripts": {
52
+ "test": "jest --silent",
53
+ "test:watch": "jest --watch",
54
+ "typed-scss-modules": "node ./dist/lib/cli.js",
55
+ "clean": "rm -rf ./dist",
56
+ "build": "npm run clean && tsc && chmod +x dist/lib/cli.js",
57
+ "check:types": "tsc --noEmit",
58
+ "check:formatting": "prettier --check '**/*.{js,json,css,md,scss,tsx,ts}'",
59
+ "check:linting": "eslint .",
60
+ "commit": "commit",
61
+ "prepare": "npm run build && husky",
62
+ "prepublishOnly": "[ \"$CI\" = \"true\" ] || (npm run build && npm test)",
63
+ "semantic-release": "semantic-release"
64
+ },
65
+ "devDependencies": {
66
+ "@commitlint/cli": "^20.2.0",
67
+ "@commitlint/config-conventional": "^20.2.0",
68
+ "@commitlint/prompt-cli": "^20.2.0",
69
+ "@swc/core": "^1.15.8",
70
+ "@swc/jest": "^0.2.39",
71
+ "@tsjam/eslint-config-recommended": "^0.2.4",
72
+ "@tsjam/swc-jest-config-recommended": "^0.1.4",
73
+ "@types/node": "^17.0.18",
74
+ "@types/reserved-words": "^0.1.0",
75
+ "@types/yargs": "^17.0.35",
76
+ "eslint": "^9.34.0",
77
+ "eslint-plugin-jest": "^29.11.0",
78
+ "husky": "^9.1.7",
79
+ "jest": "^30.2.0",
80
+ "lint-staged": "^16.2.7",
81
+ "prettier": "^3.6.2",
82
+ "prettier-plugin-organize-imports": "^3.2.4",
83
+ "sass": "^1.97.1",
84
+ "sass-embedded": "^1.97.1",
85
+ "semantic-release": "^25.0.2",
86
+ "ts-node": "^10.9.2",
87
+ "typescript": "^5.8.3"
88
+ },
89
+ "peerDependencies": {
90
+ "sass": "^1.97.1",
91
+ "sass-embedded": "^1.97.1"
92
+ },
93
+ "peerDependenciesMeta": {
94
+ "sass": {
95
+ "optional": true
96
+ },
97
+ "sass-embedded": {
98
+ "optional": true
99
+ }
100
+ },
101
+ "dependencies": {
102
+ "bundle-require": "^5.1.0",
103
+ "chalk": "4.1.2",
104
+ "change-case": "^4.1.2",
105
+ "chokidar": "^3.6.0",
106
+ "esbuild": "^0.27.2",
107
+ "glob": "^13.0.0",
108
+ "joycon": "^3.1.1",
109
+ "postcss": "^8.4.49",
110
+ "postcss-modules": "^6.0.1",
111
+ "reserved-words": "^0.1.2",
112
+ "slash": "^3.0.0",
113
+ "yargs": "^17.7.2"
114
+ },
115
+ "lint-staged": {
116
+ "*.{js,json,css,md,scss,tsx,ts}": [
117
+ "prettier --write"
118
+ ]
119
+ }
120
+ }