Skip to content

Commit

Permalink
feat: add --allowArbitraryExtensions option (compatible with the TS 5…
Browse files Browse the repository at this point in the history
….0 feature of the same name)
  • Loading branch information
kohlmannj-nyt authored and skovy committed Jan 1, 2025
1 parent c60f74e commit 59c3422
Show file tree
Hide file tree
Showing 10 changed files with 86 additions and 3 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,16 @@ Define a [single custom SASS importer or an array of SASS importers](https://git

Refer to [`lib/sass/importer.ts`](/blob/master/lib/sass/importer.ts) for more details and the `node-sass` and `sass` importer type definitions.

### `--allowArbitraryExtensions`

- **Type**: `boolean`
- **Default**: `false`
- **Example**: `typed-scss-modules src --allowArbitraryExtensions`

Output filenames that will be compatible with the "arbitrary file extensions" feature that was introduced in TypeScript 5.0. See [the docs](https://www.typescriptlang.org/tsconfig#allowArbitraryExtensions) for more info.

In essence, the `*.scss.d.ts` extension now becomes `*.d.scss.ts` so that you can import CSS modules in projects using ESM module resolution.

## Examples

For examples of how this tool can be used and configured, see the `examples` directory:
Expand Down
1 change: 1 addition & 0 deletions __tests__/core/generate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ describeAllImplementations((implementation) => {
updateStaleOnly: false,
logLevel: "verbose",
outputFolder: null,
allowArbitraryExtensions: false,
});

expect(fs.writeFileSync).toHaveBeenCalledTimes(6);
Expand Down
5 changes: 5 additions & 0 deletions __tests__/core/list-different.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ describeAllImplementations((implementation) => {
updateStaleOnly: false,
logLevel: "verbose",
outputFolder: null,
allowArbitraryExtensions: false,
});

expect(exit).toHaveBeenCalledWith(1);
Expand Down Expand Up @@ -67,6 +68,7 @@ describeAllImplementations((implementation) => {
logLevel: "verbose",
nameFormat: ["kebab"],
outputFolder: null,
allowArbitraryExtensions: false,
});

expect(console.log).toHaveBeenCalledTimes(1);
Expand All @@ -93,6 +95,7 @@ describeAllImplementations((implementation) => {
updateStaleOnly: false,
logLevel: "verbose",
outputFolder: null,
allowArbitraryExtensions: false,
});

expect(exit).not.toHaveBeenCalled();
Expand All @@ -116,6 +119,7 @@ describeAllImplementations((implementation) => {
updateStaleOnly: false,
logLevel: "verbose",
outputFolder: null,
allowArbitraryExtensions: false,
});

expect(exit).toHaveBeenCalledWith(1);
Expand Down Expand Up @@ -146,6 +150,7 @@ describeAllImplementations((implementation) => {
updateStaleOnly: false,
logLevel: "verbose",
outputFolder: null,
allowArbitraryExtensions: false,
});

expect(exit).not.toHaveBeenCalled();
Expand Down
1 change: 1 addition & 0 deletions __tests__/core/list-files-and-perform-sanity-check.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const options: ConfigOptions = {
updateStaleOnly: false,
logLevel: "verbose",
outputFolder: null,
allowArbitraryExtensions: false,
};

describe("listAllFilesAndPerformSanityCheck", () => {
Expand Down
42 changes: 42 additions & 0 deletions __tests__/core/write-file.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ describeAllImplementations((implementation) => {
updateStaleOnly: false,
logLevel: "verbose",
outputFolder: null,
allowArbitraryExtensions: false,
});

const expectedPath = path.join(
Expand All @@ -51,6 +52,40 @@ describeAllImplementations((implementation) => {
);
});

it("writes the corresponding type definitions for a file and logs when allowArbitraryExtensions is set", async () => {
const testFile = path.resolve(__dirname, "..", "dummy-styles/style.scss");

await writeFile(testFile, {
banner: "",
watch: false,
ignoreInitial: false,
exportType: "named",
exportTypeName: "ClassNames",
exportTypeInterface: "Styles",
listDifferent: false,
ignore: [],
implementation,
quoteType: "single",
updateStaleOnly: false,
logLevel: "verbose",
outputFolder: null,
allowArbitraryExtensions: true,
});

const expectedPath = path.join(
process.cwd(),
"__tests__/dummy-styles/style.d.scss.ts"
);

expect(fs.writeFileSync).toHaveBeenCalledWith(
expectedPath,
"export declare const someClass: string;\n"
);
expect(console.log).toHaveBeenCalledWith(
expect.stringContaining(`[GENERATED TYPES] ${expectedPath}`)
);
});

it("skips files with no classes", async () => {
const testFile = path.resolve(__dirname, "..", "dummy-styles/empty.scss");

Expand All @@ -68,6 +103,7 @@ describeAllImplementations((implementation) => {
updateStaleOnly: false,
logLevel: "verbose",
outputFolder: null,
allowArbitraryExtensions: false,
});

expect(fs.writeFileSync).not.toHaveBeenCalled();
Expand Down Expand Up @@ -111,6 +147,7 @@ describeAllImplementations((implementation) => {
updateStaleOnly: false,
logLevel: "verbose",
outputFolder: null,
allowArbitraryExtensions: false,
});

expect(fs.unlinkSync).toHaveBeenCalledWith(existingTypes);
Expand Down Expand Up @@ -142,6 +179,7 @@ describeAllImplementations((implementation) => {
updateStaleOnly: false,
logLevel: "verbose",
outputFolder: "__generated__",
allowArbitraryExtensions: false,
});

const expectedPath = path.join(
Expand Down Expand Up @@ -200,6 +238,7 @@ describeAllImplementations((implementation) => {
updateStaleOnly: true,
logLevel: "verbose",
outputFolder: null,
allowArbitraryExtensions: false,
});

expect(fs.writeFileSync).not.toHaveBeenCalled();
Expand Down Expand Up @@ -235,6 +274,7 @@ describeAllImplementations((implementation) => {
updateStaleOnly: true,
logLevel: "verbose",
outputFolder: null,
allowArbitraryExtensions: false,
});

expect(fs.writeFileSync).toHaveBeenCalled();
Expand All @@ -259,6 +299,7 @@ describeAllImplementations((implementation) => {
updateStaleOnly: true,
logLevel: "verbose",
outputFolder: null,
allowArbitraryExtensions: false,
});

expect(fs.writeFileSync).not.toHaveBeenCalled();
Expand All @@ -281,6 +322,7 @@ describeAllImplementations((implementation) => {
updateStaleOnly: true,
logLevel: "verbose",
outputFolder: null,
allowArbitraryExtensions: false,
});

expect(fs.statSync).not.toHaveBeenCalledWith(testFile);
Expand Down
9 changes: 9 additions & 0 deletions __tests__/load.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ describe("#mergeOptions", () => {
logLevel: "silent",
outputFolder: "__generated__",
banner: "// override",
allowArbitraryExtensions: true,
},
{}
)
Expand All @@ -69,6 +70,7 @@ describe("#mergeOptions", () => {
logLevel: "silent",
outputFolder: "__generated__",
banner: "// override",
allowArbitraryExtensions: true,
});
});

Expand All @@ -94,6 +96,7 @@ describe("#mergeOptions", () => {
banner: "// override",
outputFolder: "__generated__",
importer,
allowArbitraryExtensions: true,
}
)
).toEqual({
Expand All @@ -112,6 +115,7 @@ describe("#mergeOptions", () => {
banner: "// override",
outputFolder: "__generated__",
importer,
allowArbitraryExtensions: true,
});
});

Expand All @@ -135,6 +139,7 @@ describe("#mergeOptions", () => {
logLevel: "silent",
banner: "// override",
outputFolder: "__cli-generated__",
allowArbitraryExtensions: true,
},
{
nameFormat: ["param"],
Expand Down Expand Up @@ -170,6 +175,7 @@ describe("#mergeOptions", () => {
banner: "// override",
outputFolder: "__cli-generated__",
importer,
allowArbitraryExtensions: true,
});
});

Expand All @@ -195,6 +201,7 @@ describe("#mergeOptions", () => {
logLevel: "silent",
banner: undefined,
outputFolder: "__cli-generated__",
allowArbitraryExtensions: true,
},
{
aliases: {},
Expand All @@ -214,6 +221,7 @@ describe("#mergeOptions", () => {
banner: "// banner",
outputFolder: "__generated__",
importer,
allowArbitraryExtensions: false,
}
)
).toEqual({
Expand All @@ -234,6 +242,7 @@ describe("#mergeOptions", () => {
banner: "// banner",
outputFolder: "__cli-generated__",
importer,
allowArbitraryExtensions: true,
});
});
});
5 changes: 5 additions & 0 deletions lib/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@ const { _: patterns, ...rest } = yargs
describe:
"Inserts text at the top of every output file for documentation purposes.",
})
.options("allowArbitraryExtensions", {
boolean: true,
describe:
'Output filenames that will be compatible with the "arbitrary file extensions" feature that was introduced in TypeScript 5.0.',
})
.parseSync();

// eslint-disable-next-line @typescript-eslint/no-floating-promises
Expand Down
1 change: 1 addition & 0 deletions lib/core/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface CLIOptions extends Exclude<SASSOptions, CLIOnlyOptions> {
watch: boolean;
logLevel: LogLevel;
outputFolder: string | null;
allowArbitraryExtensions: boolean;
}

export interface ConfigOptions extends CLIOptions, SASSOptions {}
1 change: 1 addition & 0 deletions lib/load.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export const DEFAULT_OPTIONS: CLIOptions = {
logLevel: logLevelDefault,
banner: bannerTypeDefault,
outputFolder: null,
allowArbitraryExtensions: false,
};

const removedUndefinedValues = <Obj extends Record<string, unknown>>(
Expand Down
14 changes: 11 additions & 3 deletions lib/typescript/get-type-definition-path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,24 @@ export const getTypeDefinitionPath = (
file: string,
options: ConfigOptions
): string => {
let resolvedPath = file;

if (options.outputFolder) {
const relativePath = path.relative(CURRENT_WORKING_DIRECTORY, file);
const resolvedPath = path.resolve(
resolvedPath = path.resolve(
CURRENT_WORKING_DIRECTORY,
options.outputFolder,
relativePath
);
}

return `${resolvedPath}.d.ts`;
if (options.allowArbitraryExtensions) {
const resolvedDirname = path.dirname(resolvedPath);
// Note: `ext` includes a leading period (e.g. '.scss')
const { name, ext } = path.parse(resolvedPath);
// @see https://www.typescriptlang.org/tsconfig/#allowArbitraryExtensions
return path.join(resolvedDirname, `${name}.d${ext}.ts`);
} else {
return `${file}.d.ts`;
return `${resolvedPath}.d.ts`;
}
};

0 comments on commit 59c3422

Please sign in to comment.