@ttsc/factory 0.19.3 → 0.20.1

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 (56) hide show
  1. package/README.md +2 -0
  2. package/lib/TsPrinter.d.ts +124 -17
  3. package/lib/TsPrinter.js +414 -106
  4. package/lib/TsPrinter.js.map +1 -1
  5. package/lib/TsPrinter.mjs +413 -107
  6. package/lib/TsPrinter.mjs.map +1 -1
  7. package/lib/ast/expressions/Expression.d.ts +5 -1
  8. package/lib/ast/imports/ImportClause.d.ts +10 -2
  9. package/lib/ast/jsdoc/JSDocImportTag.d.ts +3 -0
  10. package/lib/ast/types/ImportTypeNode.d.ts +3 -0
  11. package/lib/factory/expressions/createComma.d.ts +3 -3
  12. package/lib/factory/expressions/createComma.js +3 -3
  13. package/lib/factory/expressions/createComma.mjs +3 -3
  14. package/lib/factory/imports/createImportClause.d.ts +9 -3
  15. package/lib/factory/imports/createImportClause.js +8 -3
  16. package/lib/factory/imports/createImportClause.js.map +1 -1
  17. package/lib/factory/imports/createImportClause.mjs +8 -3
  18. package/lib/factory/imports/createImportClause.mjs.map +1 -1
  19. package/lib/factory/jsdoc/createJSDocImportTag.d.ts +3 -2
  20. package/lib/factory/jsdoc/createJSDocImportTag.js +3 -1
  21. package/lib/factory/jsdoc/createJSDocImportTag.js.map +1 -1
  22. package/lib/factory/jsdoc/createJSDocImportTag.mjs +3 -1
  23. package/lib/factory/jsdoc/createJSDocImportTag.mjs.map +1 -1
  24. package/lib/factory/types/createImportTypeNode.d.ts +8 -2
  25. package/lib/factory/types/createImportTypeNode.js +13 -1
  26. package/lib/factory/types/createImportTypeNode.js.map +1 -1
  27. package/lib/factory/types/createImportTypeNode.mjs +13 -1
  28. package/lib/factory/types/createImportTypeNode.mjs.map +1 -1
  29. package/lib/internal/doc.d.ts +13 -0
  30. package/lib/internal/doc.js +31 -4
  31. package/lib/internal/doc.js.map +1 -1
  32. package/lib/internal/doc.mjs +30 -4
  33. package/lib/internal/doc.mjs.map +1 -1
  34. package/lib/syntax/NodeFlags.d.ts +6 -4
  35. package/lib/syntax/NodeFlags.js +6 -4
  36. package/lib/syntax/NodeFlags.js.map +1 -1
  37. package/lib/syntax/NodeFlags.mjs +6 -4
  38. package/lib/syntax/NodeFlags.mjs.map +1 -1
  39. package/lib/syntax/SyntaxKind.d.ts +2 -0
  40. package/lib/syntax/SyntaxKind.js +6 -0
  41. package/lib/syntax/SyntaxKind.js.map +1 -1
  42. package/lib/syntax/SyntaxKind.mjs +6 -0
  43. package/lib/syntax/SyntaxKind.mjs.map +1 -1
  44. package/package.json +1 -1
  45. package/src/TsPrinter.ts +489 -121
  46. package/src/ast/expressions/Expression.ts +8 -0
  47. package/src/ast/imports/ImportClause.ts +10 -2
  48. package/src/ast/jsdoc/JSDocImportTag.ts +4 -0
  49. package/src/ast/types/ImportTypeNode.ts +4 -0
  50. package/src/factory/expressions/createComma.ts +3 -3
  51. package/src/factory/imports/createImportClause.ts +12 -6
  52. package/src/factory/jsdoc/createJSDocImportTag.ts +5 -1
  53. package/src/factory/types/createImportTypeNode.ts +21 -3
  54. package/src/internal/doc.ts +30 -3
  55. package/src/syntax/NodeFlags.ts +6 -4
  56. package/src/syntax/SyntaxKind.ts +7 -0
@@ -1,3 +1,6 @@
1
+ import type { JsxElement } from "../jsx/JsxElement";
2
+ import type { JsxFragment } from "../jsx/JsxFragment";
3
+ import type { JsxSelfClosingElement } from "../jsx/JsxSelfClosingElement";
1
4
  import type { Identifier } from "../names/Identifier";
2
5
  import type { Token } from "../names/Token";
3
6
  import type { ArrayLiteralExpression } from "./ArrayLiteralExpression";
@@ -24,6 +27,7 @@ import type { NumericLiteral } from "./NumericLiteral";
24
27
  import type { ObjectLiteralExpression } from "./ObjectLiteralExpression";
25
28
  import type { OmittedExpression } from "./OmittedExpression";
26
29
  import type { ParenthesizedExpression } from "./ParenthesizedExpression";
30
+ import type { PartiallyEmittedExpression } from "./PartiallyEmittedExpression";
27
31
  import type { PostfixUnaryExpression } from "./PostfixUnaryExpression";
28
32
  import type { PrefixUnaryExpression } from "./PrefixUnaryExpression";
29
33
  import type { PropertyAccessChain } from "./PropertyAccessChain";
@@ -61,6 +65,9 @@ export type Expression =
61
65
  | ElementAccessExpression
62
66
  | FunctionExpression
63
67
  | Identifier
68
+ | JsxElement
69
+ | JsxFragment
70
+ | JsxSelfClosingElement
64
71
  | MetaProperty
65
72
  | NewExpression
66
73
  | NoSubstitutionTemplateLiteral
@@ -70,6 +77,7 @@ export type Expression =
70
77
  | ObjectLiteralExpression
71
78
  | OmittedExpression
72
79
  | ParenthesizedExpression
80
+ | PartiallyEmittedExpression
73
81
  | PostfixUnaryExpression
74
82
  | PrefixUnaryExpression
75
83
  | PropertyAccessChain
@@ -1,3 +1,4 @@
1
+ import type { SyntaxKind } from "../../syntax";
1
2
  import type { Identifier } from "../names/Identifier";
2
3
  import type { NamedImports } from "./NamedImports";
3
4
  import type { NamespaceImport } from "./NamespaceImport";
@@ -13,8 +14,15 @@ export interface ImportClause {
13
14
  /** Discriminant tag; always `"ImportClause"`. */
14
15
  kind: "ImportClause";
15
16
 
16
- /** Whether this is a type-only import/export. */
17
- isTypeOnly: boolean;
17
+ /**
18
+ * The keyword between `import` and the bindings, when there is one.
19
+ *
20
+ * `TypeKeyword` is the type-only import; `DeferKeyword` is `import defer`.
21
+ * Upstream calls the pair `ImportPhaseModifierSyntaxKind`, and this field
22
+ * replaced a `isTypeOnly: boolean` that could not express the second phase at
23
+ * all.
24
+ */
25
+ phaseModifier?: SyntaxKind.TypeKeyword | SyntaxKind.DeferKeyword;
18
26
 
19
27
  /** The name. */
20
28
  name?: Identifier;
@@ -1,4 +1,5 @@
1
1
  import type { Expression } from "../expressions/Expression";
2
+ import type { ImportAttributes } from "../imports/ImportAttributes";
2
3
  import type { ImportClause } from "../imports/ImportClause";
3
4
  import type { Identifier } from "../names/Identifier";
4
5
  import type { JSDocComment } from "./JSDocComment";
@@ -23,6 +24,9 @@ export interface JSDocImportTag {
23
24
  /** The module specifier. */
24
25
  moduleSpecifier: Expression;
25
26
 
27
+ /** The `with { … }` import attributes, if any. */
28
+ attributes?: ImportAttributes;
29
+
26
30
  /** The trailing comment, if any. */
27
31
  comment?: string | readonly JSDocComment[];
28
32
  }
@@ -1,3 +1,4 @@
1
+ import type { ImportAttributes } from "../imports/ImportAttributes";
1
2
  import type { EntityName } from "../names/EntityName";
2
3
  import type { TypeNode } from "./TypeNode";
3
4
 
@@ -15,6 +16,9 @@ export interface ImportTypeNode {
15
16
  /** Argument. */
16
17
  argument: TypeNode;
17
18
 
19
+ /** The `with { … }` import attributes, if any. */
20
+ attributes?: ImportAttributes;
21
+
18
22
  /** Qualifier. */
19
23
  qualifier?: EntityName;
20
24
 
@@ -7,9 +7,9 @@ import { createBinaryExpression } from "./createBinaryExpression";
7
7
  * which evaluates both operands and yields the right one.
8
8
  *
9
9
  * Shorthand for {@link createBinaryExpression} with the `CommaToken` operator.
10
- * The printer treats the comma like any other binary operator and surrounds it
11
- * with a single space on each side, so the output has a space before the
12
- * comma.
10
+ * The comma attaches to the operand before it, the way every other producer
11
+ * writes it {@link createCommaListExpression}, the legacy printer, and
12
+ * Prettier all emit `a, b`.
13
13
  *
14
14
  * Given operands `a` and `b`, the printer emits:
15
15
  *
@@ -4,6 +4,7 @@ import type {
4
4
  NamedImports,
5
5
  NamespaceImport,
6
6
  } from "../../ast";
7
+ import { SyntaxKind } from "../../syntax";
7
8
  import { make } from "../internal/make";
8
9
 
9
10
  /**
@@ -13,7 +14,12 @@ import { make } from "../internal/make";
13
14
  * The `name` is the default-import binding, if any. The `namedBindings` slot
14
15
  * holds either a {@link NamedImports} brace group or a {@link NamespaceImport}. A
15
16
  * default binding and named bindings can appear together, joined by a comma.
16
- * Set `isTypeOnly` to prefix the clause with `type`.
17
+ *
18
+ * The `phaseModifier` is the keyword between `import` and the bindings:
19
+ * `SyntaxKind.TypeKeyword` for a type-only import, `SyntaxKind.DeferKeyword`
20
+ * for `import defer`. Upstream calls the pair `ImportPhaseModifierSyntaxKind`
21
+ * and takes it in this position; this factory took a boolean here, which read a
22
+ * modern first argument as `isTypeOnly` and could not express `defer` at all.
17
23
  *
18
24
  * Given a default binding `Def` plus named import `a`, this prints:
19
25
  *
@@ -22,13 +28,13 @@ import { make } from "../internal/make";
22
28
  * ```
23
29
  *
24
30
  * @author Jeongho Nam - https://github.com/samchon
25
- * @param isTypeOnly Whether this is a type-only import/export.
31
+ * @param phaseModifier The `type` or `defer` keyword, if any.
26
32
  * @param name The name.
27
33
  * @param namedBindings The named or namespace bindings, if any.
28
34
  * @returns The created {@link ImportClause}.
29
35
  */
30
36
  export const createImportClause = (
31
- isTypeOnly: boolean,
32
- name: Identifier | undefined,
33
- namedBindings: NamedImports | NamespaceImport | undefined,
34
- ): ImportClause => make("ImportClause", { isTypeOnly, name, namedBindings });
37
+ phaseModifier?: SyntaxKind.TypeKeyword | SyntaxKind.DeferKeyword,
38
+ name?: Identifier,
39
+ namedBindings?: NamedImports | NamespaceImport,
40
+ ): ImportClause => make("ImportClause", { phaseModifier, name, namedBindings });
@@ -1,6 +1,7 @@
1
1
  import type {
2
2
  Expression,
3
3
  Identifier,
4
+ ImportAttributes,
4
5
  ImportClause,
5
6
  JSDocComment,
6
7
  JSDocImportTag,
@@ -27,6 +28,7 @@ import { createIdentifier } from "../names/createIdentifier";
27
28
  * @param tagName The tag name; defaults to `import`.
28
29
  * @param importClause The import clause, if any.
29
30
  * @param moduleSpecifier The module specifier.
31
+ * @param attributes The `with { … }` import attributes, if any.
30
32
  * @param comment The trailing comment, if any.
31
33
  * @returns The created {@link JSDocImportTag}.
32
34
  */
@@ -34,11 +36,13 @@ export const createJSDocImportTag = (
34
36
  tagName: Identifier | undefined,
35
37
  importClause: ImportClause | undefined,
36
38
  moduleSpecifier: Expression,
37
- comment?: string | readonly JSDocComment[],
39
+ attributes?: ImportAttributes,
40
+ comment?: readonly JSDocComment[],
38
41
  ): JSDocImportTag =>
39
42
  make("JSDocImportTag", {
40
43
  tagName: tagName ?? createIdentifier("import"),
41
44
  importClause,
42
45
  moduleSpecifier,
46
+ attributes,
43
47
  comment,
44
48
  });
@@ -1,9 +1,20 @@
1
- import type { EntityName, ImportTypeNode, TypeNode } from "../../ast";
1
+ import type {
2
+ EntityName,
3
+ ImportAttributes,
4
+ ImportTypeNode,
5
+ TypeNode,
6
+ } from "../../ast";
2
7
  import { make } from "../internal/make";
3
8
 
4
9
  /**
5
10
  * Create an {@link ImportTypeNode}: an `import("module").Qualifier<Args>` type.
6
11
  *
12
+ * The parameter order is upstream's: `isTypeOf` first, then the module
13
+ * specifier, then the `with { … }` attributes, then the qualifier and type
14
+ * arguments. This factory used to put `isTypeOf` last and omit `attributes`
15
+ * entirely, so a caller ported from `ts.factory` bound every argument to the
16
+ * wrong slot.
17
+ *
7
18
  * The `argument` is the module specifier inside `import(...)`. A `qualifier`
8
19
  * adds a `.Member` access, and type arguments add `<...>`. When `isTypeOf` is
9
20
  * true the whole thing is prefixed with `typeof ` to query a value's type.
@@ -23,9 +34,16 @@ import { make } from "../internal/make";
23
34
  * @returns The created {@link ImportTypeNode}.
24
35
  */
25
36
  export const createImportTypeNode = (
37
+ isTypeOf: boolean | undefined,
26
38
  argument: TypeNode,
39
+ attributes?: ImportAttributes,
27
40
  qualifier?: EntityName,
28
41
  typeArguments?: readonly TypeNode[],
29
- isTypeOf: boolean = false,
30
42
  ): ImportTypeNode =>
31
- make("ImportTypeNode", { argument, qualifier, typeArguments, isTypeOf });
43
+ make("ImportTypeNode", {
44
+ argument,
45
+ attributes,
46
+ qualifier,
47
+ typeArguments,
48
+ isTypeOf: isTypeOf === true,
49
+ });
@@ -12,6 +12,7 @@
12
12
  */
13
13
  export type Doc =
14
14
  | string
15
+ | { type: "raw"; text: string }
15
16
  | { type: "concat"; parts: Doc[] }
16
17
  | { type: "line" }
17
18
  | { type: "softline" }
@@ -22,6 +23,16 @@ export type Doc =
22
23
 
23
24
  /** Concatenate documents. */
24
25
  export const concat = (parts: Doc[]): Doc => ({ type: "concat", parts });
26
+ /**
27
+ * Literal text whose trailing whitespace is content, not layout.
28
+ *
29
+ * {@link printDocToString} strips spaces and tabs from the end of a line before
30
+ * writing a newline, which is right for generated code and wrong for the one
31
+ * node emitted as unquoted source text, `JsxText`: a trimmed trailing space
32
+ * there deletes a JSX separator and changes what the component renders. Text
33
+ * emitted through this node is never trimmed.
34
+ */
35
+ export const raw = (text: string): Doc => ({ type: "raw", text });
25
36
  /** A group: printed flat when it fits, broken otherwise. */
26
37
  export const group = (doc: Doc, shouldBreak: boolean = false): Doc => ({
27
38
  type: "group",
@@ -100,6 +111,9 @@ const fits = (next: Cmd, rest: readonly Cmd[], remaining: number): boolean => {
100
111
  continue;
101
112
  }
102
113
  switch (doc.type) {
114
+ case "raw":
115
+ width -= doc.text.length;
116
+ break;
103
117
  case "concat":
104
118
  for (let i = doc.parts.length - 1; i >= 0; i--)
105
119
  cmds.push([ind, mode, doc.parts[i]!]);
@@ -143,21 +157,34 @@ export const printDocToString = (
143
157
  const { printWidth, newLine, indent: tab } = options;
144
158
  const out: string[] = [];
145
159
  let pos = 0;
160
+ // whether the tail of `out` is raw text, whose trailing whitespace is content
161
+ let rawTail = false;
146
162
  const cmds: Cmd[] = [[0, MODE_BREAK, doc]];
147
163
  const newlineTo = (ind: number): void => {
148
- if (out.length)
164
+ if (out.length && !rawTail)
149
165
  out[out.length - 1] = out[out.length - 1]!.replace(/[ \t]+$/, "");
150
166
  out.push(newLine + tab.repeat(ind));
151
167
  pos = tab.length * ind;
168
+ rawTail = false;
152
169
  };
153
170
  while (cmds.length) {
154
171
  const [ind, mode, d] = cmds.pop()!;
155
172
  if (typeof d === "string") {
156
- out.push(d);
157
- pos += d.length;
173
+ // an empty string contributes nothing but would become the tail that
174
+ // `newlineTo` trims, hiding the real end of the line behind it
175
+ if (d.length !== 0) {
176
+ out.push(d);
177
+ pos += d.length;
178
+ rawTail = false;
179
+ }
158
180
  continue;
159
181
  }
160
182
  switch (d.type) {
183
+ case "raw":
184
+ out.push(d.text);
185
+ pos += d.text.length;
186
+ rawTail = true;
187
+ break;
161
188
  case "concat":
162
189
  for (let i = d.parts.length - 1; i >= 0; i--)
163
190
  cmds.push([ind, mode, d.parts[i]!]);
@@ -4,10 +4,12 @@
4
4
  *
5
5
  * An outline of the relevant subset of the legacy `ts.NodeFlags`. Each member's
6
6
  * value is the keyword it implies (`Let = "let"`, `Const = "const"`), so the
7
- * printer reads it directly. `Namespace` is accepted for API parity — the
8
- * printer renders the `module` / `namespace` keyword from the module name kind,
9
- * not from this flag. A string-valued enum (deliberately not `const enum`, so
10
- * consumers compiled with `isolatedModules` can still reference its members).
7
+ * printer reads it directly. `Namespace` selects the keyword a module
8
+ * declaration with an identifier name prints: with the flag it is `namespace
9
+ * A`, without it `module A`. A string-literal name is always `module "…"`, so
10
+ * the flag says nothing there. A string-valued enum (deliberately not `const
11
+ * enum`, so consumers compiled with `isolatedModules` can still reference its
12
+ * members).
11
13
  *
12
14
  * @author Jeongho Nam - https://github.com/samchon
13
15
  */
@@ -28,6 +28,13 @@ export enum SyntaxKind {
28
28
  FalseKeyword = "false",
29
29
  ThisKeyword = "this",
30
30
 
31
+ // import phase modifiers — the two keywords that may precede an import
32
+ // clause's bindings. Upstream types them as
33
+ // `ImportPhaseModifierSyntaxKind = TypeKeyword | DeferKeyword`, and
34
+ // `createImportClause` takes one where this factory used to take a boolean.
35
+ TypeKeyword = "type",
36
+ DeferKeyword = "defer",
37
+
31
38
  // modifiers
32
39
  ExportKeyword = "export",
33
40
  DefaultKeyword = "default",