@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,6 @@
1
+ export { alerts, setAlertsLogLevel } from "./alerts.js";
2
+ export { generate } from "./generate.js";
3
+ export { listDifferent } from "./list-different.js";
4
+ export type { CLIOptions, ConfigOptions } from "./types.js";
5
+ export { watch } from "./watch.js";
6
+ export { writeFile } from "./write-file.js";
@@ -0,0 +1,5 @@
1
+ export { alerts, setAlertsLogLevel } from "./alerts.js";
2
+ export { generate } from "./generate.js";
3
+ export { listDifferent } from "./list-different.js";
4
+ export { watch } from "./watch.js";
5
+ export { writeFile } from "./write-file.js";
@@ -0,0 +1,3 @@
1
+ import type { ConfigOptions } from "./types.js";
2
+ export declare const listDifferent: (pattern: string, options: ConfigOptions) => Promise<void>;
3
+ export declare const checkFile: (file: string, options: ConfigOptions) => Promise<boolean>;
@@ -0,0 +1,42 @@
1
+ import fs from "fs";
2
+ import { fileToClassNames } from "../sass/file-to-class-names.js";
3
+ import { classNamesToTypeDefinitions, getTypeDefinitionPath, } from "../typescript/index.js";
4
+ import { alerts } from "./alerts.js";
5
+ import { listFilesAndPerformSanityChecks } from "./list-files-and-perform-sanity-checks.js";
6
+ export const listDifferent = async (pattern, options) => {
7
+ const files = listFilesAndPerformSanityChecks(pattern, options);
8
+ // Wait for all the files to be checked.
9
+ const validChecks = await Promise.all(files.map((file) => checkFile(file, options)));
10
+ if (validChecks.includes(false)) {
11
+ process.exit(1);
12
+ }
13
+ };
14
+ export const checkFile = async (file, options) => {
15
+ try {
16
+ const classNames = await fileToClassNames(file, options);
17
+ const typeDefinition = await classNamesToTypeDefinitions({
18
+ classNames: classNames,
19
+ file,
20
+ ...options,
21
+ });
22
+ if (!typeDefinition) {
23
+ // Assume if no type defs are necessary it's fine
24
+ return true;
25
+ }
26
+ const path = getTypeDefinitionPath(file, options);
27
+ if (!fs.existsSync(path)) {
28
+ alerts.error(`[INVALID TYPES] Type file needs to be generated for ${file} `);
29
+ return false;
30
+ }
31
+ const content = fs.readFileSync(path, { encoding: "utf8" });
32
+ if (content !== typeDefinition) {
33
+ alerts.error(`[INVALID TYPES] Check type definitions for ${file}`);
34
+ return false;
35
+ }
36
+ return true;
37
+ }
38
+ catch (error) {
39
+ alerts.error(`An error occurred checking ${file}:\n${JSON.stringify(error)}`);
40
+ return false;
41
+ }
42
+ };
@@ -0,0 +1,9 @@
1
+ import type { ConfigOptions } from "./types.js";
2
+ /**
3
+ * Return the files matching the given pattern and alert the user if only 0 or 1
4
+ * files matched.
5
+ *
6
+ * @param pattern the file pattern to generate type definitions for
7
+ * @param options the CLI options
8
+ */
9
+ export declare function listFilesAndPerformSanityChecks(pattern: string, options: ConfigOptions): string[];
@@ -0,0 +1,22 @@
1
+ import { globSync } from "glob";
2
+ import { alerts } from "./alerts.js";
3
+ /**
4
+ * Return the files matching the given pattern and alert the user if only 0 or 1
5
+ * files matched.
6
+ *
7
+ * @param pattern the file pattern to generate type definitions for
8
+ * @param options the CLI options
9
+ */
10
+ export function listFilesAndPerformSanityChecks(pattern, options) {
11
+ // Find all the files that match the provided pattern.
12
+ const files = globSync(pattern, { ignore: options.ignore });
13
+ if (!files || !files.length) {
14
+ alerts.error("No files found.");
15
+ }
16
+ // This case still works as expected but it's easy to do on accident so
17
+ // provide a (hopefully) helpful warning.
18
+ if (files.length === 1) {
19
+ alerts.warn(`Only 1 file found for ${pattern}. If using a glob pattern (eg: dir/**/*.scss) make sure to wrap in quotes (eg: "dir/**/*.scss").`);
20
+ }
21
+ return files;
22
+ }
@@ -0,0 +1,7 @@
1
+ import type { ConfigOptions } from "./types.js";
2
+ /**
3
+ * Given a single file remove the generated types if they exist
4
+ *
5
+ * @param file the SCSS file to generate types for
6
+ */
7
+ export declare const removeSCSSTypeDefinitionFile: (file: string, options: ConfigOptions) => void;
@@ -0,0 +1,28 @@
1
+ import fs from "fs";
2
+ import { getTypeDefinitionPath } from "../typescript/get-type-definition-path.js";
3
+ import { alerts } from "./alerts.js";
4
+ /**
5
+ * Given a single file remove the file
6
+ *
7
+ * @param file any file to remove
8
+ */
9
+ const removeFile = (file) => {
10
+ try {
11
+ if (fs.existsSync(file)) {
12
+ fs.unlinkSync(file);
13
+ alerts.success(`[REMOVED] ${file}`);
14
+ }
15
+ }
16
+ catch (error) {
17
+ alerts.error(`An error occurred removing ${file}:\n${JSON.stringify(error)}`);
18
+ }
19
+ };
20
+ /**
21
+ * Given a single file remove the generated types if they exist
22
+ *
23
+ * @param file the SCSS file to generate types for
24
+ */
25
+ export const removeSCSSTypeDefinitionFile = (file, options) => {
26
+ const path = getTypeDefinitionPath(file, options);
27
+ removeFile(path);
28
+ };
@@ -0,0 +1,21 @@
1
+ import type { SASSOptions } from "../sass/file-to-class-names.js";
2
+ import type { ExportType, LogLevel, QuoteType } from "../typescript/index.js";
3
+ type CLIOnlyOptions = Extract<keyof SASSOptions, "importer">;
4
+ export interface CLIOptions extends Exclude<SASSOptions, CLIOnlyOptions> {
5
+ banner: string;
6
+ ignore: string[];
7
+ ignoreInitial: boolean;
8
+ exportType: ExportType;
9
+ exportTypeName: string;
10
+ exportTypeInterface: string;
11
+ listDifferent: boolean;
12
+ quoteType: QuoteType;
13
+ updateStaleOnly: boolean;
14
+ watch: boolean;
15
+ logLevel: LogLevel;
16
+ outputFolder: string | null;
17
+ allowArbitraryExtensions: boolean;
18
+ }
19
+ export interface ConfigOptions extends CLIOptions, SASSOptions {
20
+ }
21
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ import type { ConfigOptions } from "./types.js";
2
+ /**
3
+ * Watch a file glob and generate the corresponding types.
4
+ *
5
+ * @param pattern the file pattern to watch for file changes or additions
6
+ * @param options the CLI options
7
+ */
8
+ export declare const watch: (pattern: string, options: ConfigOptions) => void;
@@ -0,0 +1,34 @@
1
+ import chok from "chokidar";
2
+ import { alerts } from "./alerts.js";
3
+ import { listFilesAndPerformSanityChecks } from "./list-files-and-perform-sanity-checks.js";
4
+ import { removeSCSSTypeDefinitionFile } from "./remove-file.js";
5
+ import { writeFile } from "./write-file.js";
6
+ /**
7
+ * Watch a file glob and generate the corresponding types.
8
+ *
9
+ * @param pattern the file pattern to watch for file changes or additions
10
+ * @param options the CLI options
11
+ */
12
+ export const watch = (pattern, options) => {
13
+ listFilesAndPerformSanityChecks(pattern, options);
14
+ alerts.success("Watching files...");
15
+ const watcher = chok.watch(pattern, {
16
+ ignoreInitial: options.ignoreInitial,
17
+ ignored: options.ignore,
18
+ });
19
+ watcher
20
+ .on("change", (path) => {
21
+ alerts.info(`[CHANGED] ${path}`);
22
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
23
+ writeFile(path, options);
24
+ })
25
+ .on("add", (path) => {
26
+ alerts.info(`[ADDED] ${path}`);
27
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
28
+ writeFile(path, options);
29
+ })
30
+ .on("unlink", (path) => {
31
+ alerts.info(`[REMOVED] ${path}`);
32
+ removeSCSSTypeDefinitionFile(path, options);
33
+ });
34
+ };
@@ -0,0 +1,8 @@
1
+ import type { CLIOptions } from "./types.js";
2
+ /**
3
+ * Given a single file generate the proper types.
4
+ *
5
+ * @param file the SCSS file to generate types for
6
+ * @param options the CLI options
7
+ */
8
+ export declare const writeFile: (file: string, options: CLIOptions) => Promise<void>;
@@ -0,0 +1,62 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { fileToClassNames } from "../sass/file-to-class-names.js";
4
+ import { classNamesToTypeDefinitions, getTypeDefinitionPath, } from "../typescript/index.js";
5
+ import { alerts } from "./alerts.js";
6
+ import { removeSCSSTypeDefinitionFile } from "./remove-file.js";
7
+ /**
8
+ * Given a single file generate the proper types.
9
+ *
10
+ * @param file the SCSS file to generate types for
11
+ * @param options the CLI options
12
+ */
13
+ export const writeFile = async (file, options) => {
14
+ try {
15
+ const classNames = await fileToClassNames(file, options);
16
+ const typeDefinition = await classNamesToTypeDefinitions({
17
+ classNames,
18
+ file,
19
+ ...options,
20
+ });
21
+ const typesPath = getTypeDefinitionPath(file, options);
22
+ const typesExist = fs.existsSync(typesPath);
23
+ // Avoid outputting empty type definition files.
24
+ // If the file exists and the type definition is now empty, remove the file.
25
+ if (!typeDefinition) {
26
+ if (typesExist) {
27
+ removeSCSSTypeDefinitionFile(file, options);
28
+ }
29
+ else {
30
+ alerts.notice(`[NO GENERATED TYPES] ${file}`);
31
+ }
32
+ return;
33
+ }
34
+ // Avoid re-writing the file if it hasn't changed.
35
+ // First by checking the file modification time, then
36
+ // by comparing the file contents.
37
+ if (options.updateStaleOnly && typesExist) {
38
+ const fileModified = fs.statSync(file).mtime;
39
+ const typeDefinitionModified = fs.statSync(typesPath).mtime;
40
+ if (fileModified < typeDefinitionModified) {
41
+ return;
42
+ }
43
+ const existingTypeDefinition = fs.readFileSync(typesPath, "utf8");
44
+ if (existingTypeDefinition === typeDefinition) {
45
+ return;
46
+ }
47
+ }
48
+ // Files can be written to arbitrary directories and need to
49
+ // be nested to match the project structure so it's possible
50
+ // there are multiple directories that need to be created.
51
+ const dirname = path.dirname(typesPath);
52
+ if (!fs.existsSync(dirname)) {
53
+ fs.mkdirSync(dirname, { recursive: true });
54
+ }
55
+ fs.writeFileSync(typesPath, typeDefinition);
56
+ alerts.success(`[GENERATED TYPES] ${typesPath}`);
57
+ }
58
+ catch (error) {
59
+ const errorMessage = error instanceof Error ? error.message : String(error);
60
+ alerts.error(`Write File '${file}' error: ${errorMessage}`);
61
+ }
62
+ };
@@ -0,0 +1,14 @@
1
+ import type { AsyncCompiler, Implementation, Implementations, SyncCompiler } from "./implementations.js";
2
+ export declare const disposeAllCompilers: () => Promise<void>;
3
+ type ImplementationDetails = {
4
+ implementation: Implementations;
5
+ root: Implementation;
6
+ };
7
+ export declare const getSyncCompiler: ({ root, implementation, }: ImplementationDetails) => SyncCompiler | Pick<Implementation, "compile">;
8
+ /**
9
+ * When calling the compile functions multiple times, using a compiler instance with the sass-embedded npm package
10
+ * is much faster than using the top-level compilation methods or the sass npm package.
11
+ * @see https://sass-lang.com/documentation/js-api/functions/initasynccompiler/
12
+ */
13
+ export declare const getAsyncCompiler: ({ root, implementation, }: ImplementationDetails) => Promise<AsyncCompiler | Pick<Implementation, "compileAsync">>;
14
+ export {};
@@ -0,0 +1,49 @@
1
+ import { alerts } from "../core/index.js";
2
+ const cache = {};
3
+ export const disposeAllCompilers = async () => {
4
+ // Cleanup of any cached compilers
5
+ cache.syncCompiler?.dispose();
6
+ cache.syncCompiler = undefined;
7
+ if (cache.asyncCompiler) {
8
+ const asyncCompiler = await cache.asyncCompiler;
9
+ await asyncCompiler?.dispose();
10
+ cache.asyncCompiler = undefined;
11
+ }
12
+ };
13
+ process.on("SIGINT", async () => {
14
+ await disposeAllCompilers();
15
+ process.exit(0);
16
+ });
17
+ export const getSyncCompiler = ({ root, implementation, }) => {
18
+ /* When calling the compile functions multiple times, using a compiler instance with the sass-embedded npm package
19
+ is much faster than using the top-level compilation methods or the sass npm package */
20
+ const useCompilerInstance = implementation === "sass-embedded";
21
+ if (!useCompilerInstance) {
22
+ return root;
23
+ }
24
+ if (cache.syncCompiler) {
25
+ return cache.syncCompiler;
26
+ }
27
+ cache.syncCompiler = root.initCompiler();
28
+ alerts.info(`Using '${implementation}' Sync compiler instance for improved performance.`);
29
+ return cache.syncCompiler;
30
+ };
31
+ /**
32
+ * When calling the compile functions multiple times, using a compiler instance with the sass-embedded npm package
33
+ * is much faster than using the top-level compilation methods or the sass npm package.
34
+ * @see https://sass-lang.com/documentation/js-api/functions/initasynccompiler/
35
+ */
36
+ export const getAsyncCompiler = ({ root, implementation, }) => {
37
+ /* When calling the compile functions multiple times, using a compiler instance with the sass-embedded npm package
38
+ is much faster than using the top-level compilation methods or the sass npm package */
39
+ const useCompilerInstance = implementation === "sass-embedded";
40
+ if (!useCompilerInstance) {
41
+ return Promise.resolve(root);
42
+ }
43
+ if (cache.asyncCompiler) {
44
+ return cache.asyncCompiler;
45
+ }
46
+ cache.asyncCompiler = root.initAsyncCompiler();
47
+ alerts.info(`Using '${implementation}' Async compiler instance for improved performance.`);
48
+ return cache.asyncCompiler;
49
+ };
@@ -0,0 +1,27 @@
1
+ import sass, { AsyncCompiler as SassAsyncCompiler, Compiler as SassCompiler } from "sass";
2
+ import sassEmbedded, { AsyncCompiler as SassEmbeddedAsyncCompiler, Compiler as SassEmbeddedCompiler } from "sass-embedded";
3
+ /**
4
+ * A list of all possible SASS package implementations that can be used to
5
+ * perform the compilation and parsing of the SASS files. The expectation is
6
+ * that they provide a nearly identical API so they can be swapped out but
7
+ * all of the same logic can be reused.
8
+ */
9
+ export declare const IMPLEMENTATIONS: readonly ["sass", "sass-embedded"];
10
+ export type Implementations = (typeof IMPLEMENTATIONS)[number];
11
+ export type Implementation = typeof sass | typeof sassEmbedded;
12
+ export type SyncCompiler = SassCompiler | SassEmbeddedCompiler;
13
+ export type AsyncCompiler = SassAsyncCompiler | SassEmbeddedAsyncCompiler;
14
+ /**
15
+ * Determine which default implementation to use by checking which packages
16
+ * are actually installed and available to use.
17
+ *
18
+ * @param resolver DO NOT USE - this is unfortunately necessary only for testing.
19
+ */
20
+ export declare const getDefaultImplementation: (resolver?: RequireResolve) => Implementations;
21
+ /**
22
+ * Retrieve the desired implementation.
23
+ *
24
+ * @param implementation the desired implementation.
25
+ */
26
+ export declare const getImplementation: (implementation?: Implementations | string) => Promise<Implementation>;
27
+ export declare const getImplementationAsync: (implementation?: Implementations | string) => Promise<Implementation>;
@@ -0,0 +1,54 @@
1
+ import sass, { AsyncCompiler as SassAsyncCompiler, Compiler as SassCompiler, } from "sass";
2
+ import sassEmbedded, { AsyncCompiler as SassEmbeddedAsyncCompiler, Compiler as SassEmbeddedCompiler, } from "sass-embedded";
3
+ /**
4
+ * A list of all possible SASS package implementations that can be used to
5
+ * perform the compilation and parsing of the SASS files. The expectation is
6
+ * that they provide a nearly identical API so they can be swapped out but
7
+ * all of the same logic can be reused.
8
+ */
9
+ export const IMPLEMENTATIONS = ["sass", "sass-embedded"];
10
+ /**
11
+ * Determine which default implementation to use by checking which packages
12
+ * are actually installed and available to use.
13
+ *
14
+ * @param resolver DO NOT USE - this is unfortunately necessary only for testing.
15
+ */
16
+ export const getDefaultImplementation = (resolver) => {
17
+ let pkg = "sass";
18
+ try {
19
+ resolver ? resolver("sass") : import("sass");
20
+ }
21
+ catch (error) {
22
+ try {
23
+ resolver ? resolver("sass-embedded") : import("sass-embedded");
24
+ pkg = "sass-embedded";
25
+ }
26
+ catch (ignoreError) {
27
+ pkg = "sass";
28
+ }
29
+ }
30
+ return pkg;
31
+ };
32
+ /**
33
+ * Retrieve the desired implementation.
34
+ *
35
+ * @param implementation the desired implementation.
36
+ */
37
+ export const getImplementation = async (
38
+ // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
39
+ implementation = "sass") => {
40
+ return getImplementationAsync(implementation);
41
+ };
42
+ export const getImplementationAsync = (
43
+ // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
44
+ implementation = "sass") => {
45
+ if (implementation === "sass") {
46
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
47
+ return import("sass");
48
+ }
49
+ if (implementation === "sass-embedded") {
50
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
51
+ return import("sass-embedded");
52
+ }
53
+ throw new Error(`'${implementation}' Implementation is not supported`);
54
+ };
@@ -0,0 +1,2 @@
1
+ export * from "./compilers.js";
2
+ export * from "./implementations.js";
@@ -0,0 +1,2 @@
1
+ export * from "./compilers.js";
2
+ export * from "./implementations.js";
@@ -0,0 +1,3 @@
1
+ export type { ConfigOptions } from "./core/types.js";
2
+ export { IMPLEMENTATIONS, type Implementations, } from "./implementations/implementations.js";
3
+ export { main as default } from "./main.js";
@@ -0,0 +1,2 @@
1
+ export { IMPLEMENTATIONS, } from "./implementations/implementations.js";
2
+ export { main as default } from "./main.js";
@@ -0,0 +1,20 @@
1
+ import { type CLIOptions, type ConfigOptions } from "./core/types.js";
2
+ /**
3
+ * Load a custom config file in the project root directory with any options for this package.
4
+ *
5
+ * This supports config files in the following formats and order:
6
+ * - Named `config` export: `export const config = {}`
7
+ * - Default export: `export default {}`
8
+ * - `module.exports = {}`
9
+ */
10
+ export declare const loadConfig: () => Promise<Record<string, never> | ConfigOptions>;
11
+ export declare const DEFAULT_OPTIONS: CLIOptions;
12
+ /**
13
+ * Given both the CLI and config file options merge into a single options object.
14
+ *
15
+ * When possible, CLI options will override config file options.
16
+ *
17
+ * Some options are only available in the config file. For example, a custom function can't
18
+ * be easily defined via the CLI so some complex options are only available in the config file.
19
+ */
20
+ export declare const mergeOptions: (cliOptions: Partial<CLIOptions>, configOptions: Partial<ConfigOptions>) => ConfigOptions;
@@ -0,0 +1,87 @@
1
+ import { bundleRequire } from "bundle-require";
2
+ import JoyCon from "joycon";
3
+ import path from "path";
4
+ import { alerts } from "./core/alerts.js";
5
+ import {} from "./core/types.js";
6
+ import { getDefaultImplementation } from "./implementations/implementations.js";
7
+ import { nameFormatDefault } from "./sass/file-to-class-names.js";
8
+ import { bannerTypeDefault, exportTypeDefault, exportTypeInterfaceDefault, exportTypeNameDefault, logLevelDefault, quoteTypeDefault, } from "./typescript/index.js";
9
+ const VALID_CONFIG_FILES = [
10
+ "typed-scss-modules.config.ts",
11
+ "typed-scss-modules.config.mjs",
12
+ "typed-scss-modules.config.js",
13
+ ];
14
+ const joycon = new JoyCon.default();
15
+ /**
16
+ * Load a custom config file in the project root directory with any options for this package.
17
+ *
18
+ * This supports config files in the following formats and order:
19
+ * - Named `config` export: `export const config = {}`
20
+ * - Default export: `export default {}`
21
+ * - `module.exports = {}`
22
+ */
23
+ export const loadConfig = async () => {
24
+ const CURRENT_WORKING_DIRECTORY = process.cwd();
25
+ const configPath = await joycon.resolve(VALID_CONFIG_FILES, CURRENT_WORKING_DIRECTORY, path.parse(CURRENT_WORKING_DIRECTORY).root);
26
+ if (configPath) {
27
+ try {
28
+ const configModule = await bundleRequire({
29
+ filepath: configPath,
30
+ });
31
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
32
+ const config =
33
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
34
+ configModule.mod.config || configModule.mod.default || configModule.mod;
35
+ return config;
36
+ }
37
+ catch (error) {
38
+ alerts.error(
39
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
40
+ `An error occurred loading the config file "${configPath}":\n${error}`);
41
+ return {};
42
+ }
43
+ }
44
+ return {};
45
+ };
46
+ // Default values for all options that need defaults.
47
+ export const DEFAULT_OPTIONS = {
48
+ nameFormat: [nameFormatDefault],
49
+ implementation: getDefaultImplementation(),
50
+ exportType: exportTypeDefault,
51
+ exportTypeName: exportTypeNameDefault,
52
+ exportTypeInterface: exportTypeInterfaceDefault,
53
+ watch: false,
54
+ ignoreInitial: false,
55
+ listDifferent: false,
56
+ ignore: [],
57
+ quoteType: quoteTypeDefault,
58
+ updateStaleOnly: false,
59
+ logLevel: logLevelDefault,
60
+ banner: bannerTypeDefault,
61
+ outputFolder: null,
62
+ allowArbitraryExtensions: false,
63
+ silenceDeprecations: ["legacy-js-api"],
64
+ };
65
+ const removedUndefinedValues = (obj) => {
66
+ for (const key in obj) {
67
+ if (obj[key] === undefined) {
68
+ delete obj[key];
69
+ }
70
+ }
71
+ return obj;
72
+ };
73
+ /**
74
+ * Given both the CLI and config file options merge into a single options object.
75
+ *
76
+ * When possible, CLI options will override config file options.
77
+ *
78
+ * Some options are only available in the config file. For example, a custom function can't
79
+ * be easily defined via the CLI so some complex options are only available in the config file.
80
+ */
81
+ export const mergeOptions = (cliOptions, configOptions) => {
82
+ return {
83
+ ...DEFAULT_OPTIONS,
84
+ ...configOptions,
85
+ ...removedUndefinedValues(cliOptions),
86
+ };
87
+ };
@@ -0,0 +1,2 @@
1
+ import { type CLIOptions } from "./core/index.js";
2
+ export declare const main: (pattern: string, cliOptions: Partial<CLIOptions>) => Promise<void>;
@@ -0,0 +1,34 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import slash from "slash";
4
+ import { generate, listDifferent, setAlertsLogLevel, watch, } from "./core/index.js";
5
+ import { disposeAllCompilers } from "./implementations/compilers.js";
6
+ import { loadConfig, mergeOptions } from "./load.js";
7
+ export const main = async (pattern, cliOptions) => {
8
+ const configOptions = await loadConfig();
9
+ const options = mergeOptions(cliOptions, configOptions);
10
+ setAlertsLogLevel(options.logLevel);
11
+ // When the provided pattern is a directory construct the proper glob to find
12
+ // all .scss files within that directory. Also, add the directory to the
13
+ // included paths so any imported with a path relative to the root of the
14
+ // project still works as expected without adding many include paths.
15
+ if (fs.existsSync(pattern) && fs.lstatSync(pattern).isDirectory()) {
16
+ if (Array.isArray(options.loadPaths)) {
17
+ options.loadPaths.push(pattern);
18
+ }
19
+ else {
20
+ options.loadPaths = [pattern];
21
+ }
22
+ // When the pattern provide is a directory, assume all .scss files within.
23
+ pattern = slash(path.resolve(pattern, "**/*.scss"));
24
+ }
25
+ if (options.listDifferent) {
26
+ await listDifferent(pattern, options);
27
+ return;
28
+ }
29
+ if (options.watch) {
30
+ watch(pattern, options);
31
+ return;
32
+ }
33
+ await generate(pattern, options).finally(() => disposeAllCompilers());
34
+ };
@@ -0,0 +1 @@
1
+ export declare function canResolvePrettier(): boolean;
@@ -0,0 +1,12 @@
1
+ // this is only extracted to a module to mock in testing as require.resolve can't be mocked.
2
+ // https://github.com/facebook/jest/issues/9543
3
+ export function canResolvePrettier() {
4
+ try {
5
+ import("prettier");
6
+ return true;
7
+ }
8
+ catch (_error) {
9
+ // cannot resolve prettier
10
+ return false;
11
+ }
12
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Try to load prettier and config from project to format input,
3
+ * fall back to input if prettier is not found or failed
4
+ *
5
+ * @param {file} file
6
+ * @param {string} input
7
+ */
8
+ export declare const attemptPrettier: (file: string, input: string) => Promise<string>;