Skip to content

Commit 673161e

Browse files
authored
feat: ability to add and remove the defer keyword in imports (#1651)
1 parent f4d117f commit 673161e

File tree

8 files changed

+293
-195
lines changed

8 files changed

+293
-195
lines changed

deno.lock

Lines changed: 9 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/common/scripts/createLibFile.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import { createDtsMinifier, folders, path, tsMorph } from "./deps.ts";
22
const { ts } = tsMorph;
33

4-
const libFilesFilePath = path.join(folders.common, "src/data/libFiles.ts");
4+
const libFilesFilePath = path.join(folders.common, "src/data/libFiles.generated.ts");
55
// todo: grab this from the TypeScript repo's tag
66
const libFolderPath = path.join(folders.root, "node_modules/typescript/lib");
77
const minifier = createDtsMinifier(ts);
88

99
let libFileText = "// dprint-ignore-file\nexport const libFiles: { fileName: string; text: string; }[] = [";
10+
const entries = Array.from(Deno.readDirSync(libFolderPath));
11+
entries.sort((a, b) => a.name.localeCompare(b.name));
1012

11-
for (const entry of Deno.readDirSync(libFolderPath)) {
13+
for (const entry of entries) {
1214
const isLibFile = entry.isFile && entry.name.startsWith("lib") && entry.name.endsWith(".d.ts");
1315
if (!isLibFile)
1416
continue;

packages/common/src/data/libFiles.ts renamed to packages/common/src/data/libFiles.generated.ts

Lines changed: 191 additions & 191 deletions
Large diffs are not rendered by default.

packages/common/src/getLibFiles.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { libFiles } from "./data/libFiles";
1+
import { libFiles } from "./data/libFiles.generated";
22
import { errors } from "./errors";
33
import { StandardizedFilePath } from "./fileSystem";
44
import { nameof } from "./utils";

packages/ts-morph/lib/ts-morph.d.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7107,6 +7107,13 @@ export declare class ImportClause extends ImportClauseBase<ts.ImportClause> {
71077107
isTypeOnly(): boolean;
71087108
/** Sets if this import declaration is type only. */
71097109
setIsTypeOnly(value: boolean): this;
7110+
/** Gets if this import clause has a defer phase modifier. */
7111+
isDeferred(): boolean;
7112+
/**
7113+
* Sets if this import declaration should have a defer keyword.
7114+
* @throws When not a namespace import.
7115+
*/
7116+
setIsDeferred(value: boolean): this;
71107117
/** Gets the default import or throws if it doesn't exit. */
71117118
getDefaultImportOrThrow(message?: string | (() => string)): Identifier;
71127119
/** Gets the default import or returns undefined if it doesn't exist. */
@@ -7134,6 +7141,13 @@ export declare class ImportDeclaration extends ImportDeclarationBase<ts.ImportDe
71347141
isTypeOnly(): boolean;
71357142
/** Sets if this import declaration is type only. */
71367143
setIsTypeOnly(value: boolean): this;
7144+
/** Gets if this import declaration has a `defer` phase modifier. */
7145+
isDeferred(): boolean;
7146+
/**
7147+
* Sets if this import declaration is a deferred import.
7148+
* @throws When the import is not a namespace import.
7149+
*/
7150+
setIsDeferred(value: boolean): this;
71377151
/** Gets the phase modifier of the import declaration. */
71387152
getPhaseModifier(): ImportPhaseModifierSyntaxKind | undefined;
71397153
/**

packages/ts-morph/src/compiler/ast/module/ImportClause.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,39 @@ export class ImportClause extends ImportClauseBase<ts.ImportClause> {
3636
return this;
3737
}
3838

39+
/** Gets if this import clause has a defer phase modifier. */
40+
isDeferred() {
41+
return this.compilerNode.phaseModifier === SyntaxKind.DeferKeyword;
42+
}
43+
44+
/**
45+
* Sets if this import declaration should have a defer keyword.
46+
* @throws When not a namespace import.
47+
*/
48+
setIsDeferred(value: boolean) {
49+
if (this.isDeferred() === value)
50+
return this;
51+
52+
if (value) {
53+
if (this.getNamespaceImport() == null)
54+
throw new Error("Cannot set an import as deferred when not a namespace import.");
55+
56+
insertIntoParentTextRange({
57+
parent: this,
58+
insertPos: this.getStart(),
59+
newText: "defer ",
60+
});
61+
} else {
62+
const deferKeyword = this.getFirstChildByKindOrThrow(ts.SyntaxKind.DeferKeyword);
63+
removeChildren({
64+
children: [deferKeyword],
65+
removeFollowingSpaces: true,
66+
});
67+
}
68+
69+
return this;
70+
}
71+
3972
/**
4073
* Gets the default import or throws if it doesn't exit.
4174
*/

packages/ts-morph/src/compiler/ast/module/ImportDeclaration.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,28 @@ export class ImportDeclaration extends ImportDeclarationBase<ts.ImportDeclaratio
3939
return this;
4040
}
4141

42+
/** Gets if this import declaration has a `defer` phase modifier. */
43+
isDeferred() {
44+
return this.getImportClause()?.isDeferred() ?? false;
45+
}
46+
47+
/**
48+
* Sets if this import declaration is a deferred import.
49+
* @throws When the import is not a namespace import.
50+
*/
51+
setIsDeferred(value: boolean) {
52+
const importClause = this.getImportClause();
53+
if (importClause == null) {
54+
if (!value)
55+
return this;
56+
else
57+
throw new errors.InvalidOperationError("Cannot set an import as deferred when there is no import clause.");
58+
}
59+
60+
importClause.setIsDeferred(value);
61+
return this;
62+
}
63+
4264
/** Gets the phase modifier of the import declaration. */
4365
getPhaseModifier(): ImportPhaseModifierSyntaxKind | undefined {
4466
return this.getImportClause()?.getPhaseModifier();

packages/ts-morph/src/tests/compiler/ast/module/importDeclarationTests.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,25 @@ describe("ImportDeclaration", () => {
875875
const text = "import defer * as feature from './some-feature.js';";
876876
const { firstChild } = getInfoFromText<ImportDeclaration>(text);
877877
expect(firstChild.getPhaseModifier()).to.equal(SyntaxKind.DeferKeyword);
878+
expect(firstChild.isDeferred()).to.equal(true);
879+
});
880+
881+
it("should add and remove the defer keyword", () => {
882+
const text = "import defer * as feature from './some-feature.js';";
883+
const { firstChild } = getInfoFromText<ImportDeclaration>(text);
884+
firstChild.setIsDeferred(false);
885+
firstChild.setIsDeferred(false);
886+
expect(firstChild.getText()).to.equal("import * as feature from './some-feature.js';");
887+
firstChild.setIsDeferred(true);
888+
firstChild.setIsDeferred(true);
889+
expect(firstChild.getText()).to.equal("import defer * as feature from './some-feature.js';");
890+
});
891+
892+
it("should error setting for non-namespace import", () => {
893+
const text = "import feature from './some-feature.js';";
894+
const { firstChild } = getInfoFromText<ImportDeclaration>(text);
895+
firstChild.setIsDeferred(false);
896+
expect(() => firstChild.setIsDeferred(true)).to.throw(Error, "Cannot set an import as deferred when not a namespace import.");
878897
});
879898
});
880899
});

0 commit comments

Comments
 (0)