@napi-rs/cli 3.10.3 → 3.10.5

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.
@@ -1,5 +1,5 @@
1
1
  import { createRequire } from 'node:module'
2
- import { dirname, relative, resolve } from 'node:path'
2
+ import { dirname, parse, relative, resolve } from 'node:path'
3
3
 
4
4
  import { sortBy } from 'es-toolkit'
5
5
  import type {
@@ -971,6 +971,220 @@ export function collectRelativeDeclarationSpecifiers(source: string): string[] {
971
971
  ]
972
972
  }
973
973
 
974
+ /**
975
+ * The first construct in `source` a CommonJS declaration file cannot carry,
976
+ * or `undefined` when copying `source` into a `.d.cts` is honest.
977
+ *
978
+ * A `.d.cts` is read under `require` module-resolution mode, so a declaration
979
+ * that means one thing in an ESM file can mean another — or be a syntax error
980
+ * — once it lands there. The generated typedef needs none of those forms: its
981
+ * imports name packages or `node:` builtins, and everything it exports is a
982
+ * named `export declare`. A `--dts-header` can carry any declaration a module
983
+ * package can write, though, and silently copying one that resolves
984
+ * differently produces a `.d.cts` describing a module the loader is not.
985
+ * Those sources keep refusing, with the offending construct named, so the
986
+ * header can be fixed or `--dts` pointed at a `.d.cts` source instead.
987
+ *
988
+ * What refuses, and why:
989
+ *
990
+ * - `export default`, in any of its spellings — `export default x`,
991
+ * `export default class`/`function`/`interface`/`enum`/`namespace`,
992
+ * `export { x as default }`, `export * as default`. In a `.d.cts` `default`
993
+ * is `module.exports.default` — a property the generated loader never
994
+ * assigns — not the whole-callable shape a default export is in ESM. Inside
995
+ * a `declare module` block `export default` describes that module's own
996
+ * shape and carries over honestly, so only top-level defaults refuse.
997
+ * - A relative specifier that can land on a mode-dependent declaration:
998
+ * `.js`-family spellings (`.js`, `.jsx`, `.ts`, `.tsx`) or extensionless
999
+ * ones, which resolve `.d.cts`-first under `require` mode and
1000
+ * `.d.mts`-first under `import` mode — possibly a different file, and
1001
+ * certainly a different interpretation of the file they find. Explicit
1002
+ * module-format extensions (`.mjs`, `.mts`, `.cjs`, `.cts`) and non-script
1003
+ * targets (`.json`, `.wasm`, `.node`, assets) resolve identically either
1004
+ * way. Bare specifiers (`buffer`, `node:stream/web`, package names) are
1005
+ * allowed — a dual-condition package's `types` entry can still diverge
1006
+ * (`import` vs `require` condition), but refusing them would defeat the
1007
+ * derivation: the generated typedef's own imports are all bare.
1008
+ * - `with { … }` attributes on a runtime `import`/`export … from` statement:
1009
+ * TS2856 in a `.cts` file. Type-only statements may keep theirs, and a
1010
+ * `resolution-mode` attribute pins the specifier to one mode — either way
1011
+ * the same mode `.d.cts` or the source's own — so it is honest again.
1012
+ *
1013
+ * Everything else — `export declare`, interfaces, enums, namespaces,
1014
+ * `export =`, `declare global`, `/// <reference path>` — reads the same on
1015
+ * both sides. One deliberate gap: `import('…')` types inside JSDoc comments
1016
+ * are not scanned — they need a separate JSDoc parse for a source class the
1017
+ * generator never emits.
1018
+ */
1019
+ export function commonJsDeclarationBarrier(source: string): string | undefined {
1020
+ const typeScript = loadTypeScript()
1021
+ const sourceFile = parseDeclarationFile(source)
1022
+
1023
+ const specifierBarrier = (
1024
+ literal: import('typescript').StringLiteralLike,
1025
+ ): string | undefined => {
1026
+ if (!literal.text.startsWith('.')) {
1027
+ return undefined
1028
+ }
1029
+ const extension = parse(literal.text).ext.toLowerCase()
1030
+ if (
1031
+ extension !== '' &&
1032
+ !['.js', '.jsx', '.ts', '.tsx'].includes(extension)
1033
+ ) {
1034
+ return undefined
1035
+ }
1036
+ return `a relative '${literal.text}' specifier`
1037
+ }
1038
+
1039
+ const hasResolutionMode = (
1040
+ attributes: import('typescript').ImportAttributes | undefined,
1041
+ ) =>
1042
+ attributes?.elements.some(
1043
+ (element) =>
1044
+ element.name.text === 'resolution-mode' &&
1045
+ typeScript.isStringLiteral(element.value) &&
1046
+ (element.value.text === 'import' || element.value.text === 'require'),
1047
+ ) === true
1048
+
1049
+ const hasDefaultModifier = (node: Node) =>
1050
+ typeScript.canHaveModifiers(node) === true &&
1051
+ typeScript
1052
+ .getModifiers(node)
1053
+ ?.some(
1054
+ (modifier) => modifier.kind === typeScript.SyntaxKind.DefaultKeyword,
1055
+ ) === true
1056
+
1057
+ const isTypeOnlyStatement = (
1058
+ node:
1059
+ | import('typescript').ImportDeclaration
1060
+ | import('typescript').ExportDeclaration,
1061
+ ) =>
1062
+ typeScript.isImportDeclaration(node)
1063
+ ? node.importClause?.isTypeOnly === true
1064
+ : node.isTypeOnly === true
1065
+
1066
+ let barrier: string | undefined
1067
+ const visit = (node: Node, insideModuleDeclaration: boolean): void => {
1068
+ if (barrier !== undefined) {
1069
+ return
1070
+ }
1071
+ if (!insideModuleDeclaration && hasDefaultModifier(node)) {
1072
+ barrier = 'an `export default` declaration'
1073
+ return
1074
+ }
1075
+ if (typeScript.isModuleDeclaration(node)) {
1076
+ if (typeScript.isStringLiteralLike(node.name)) {
1077
+ barrier = specifierBarrier(node.name)
1078
+ }
1079
+ if (barrier === undefined) {
1080
+ // A `declare module`/`declare global` block's statements describe that
1081
+ // module's own shape, so a `default` export inside one is honest; its
1082
+ // specifiers still resolve relative to this file.
1083
+ typeScript.forEachChild(node, (child) => visit(child, true))
1084
+ }
1085
+ return
1086
+ }
1087
+ if (
1088
+ typeScript.isImportDeclaration(node) ||
1089
+ typeScript.isExportDeclaration(node)
1090
+ ) {
1091
+ if (node.attributes !== undefined && !isTypeOnlyStatement(node)) {
1092
+ barrier = '`with` attributes on a runtime module statement'
1093
+ return
1094
+ }
1095
+ if (
1096
+ node.moduleSpecifier !== undefined &&
1097
+ typeScript.isStringLiteralLike(node.moduleSpecifier) &&
1098
+ !hasResolutionMode(node.attributes)
1099
+ ) {
1100
+ barrier = specifierBarrier(node.moduleSpecifier)
1101
+ if (barrier !== undefined) {
1102
+ return
1103
+ }
1104
+ }
1105
+ if (
1106
+ !insideModuleDeclaration &&
1107
+ typeScript.isExportDeclaration(node) &&
1108
+ node.exportClause !== undefined &&
1109
+ ((typeScript.isNamedExports(node.exportClause) &&
1110
+ node.exportClause.elements.some(
1111
+ (element) => element.name.text === 'default',
1112
+ )) ||
1113
+ (typeScript.isNamespaceExport(node.exportClause) &&
1114
+ node.exportClause.name.text === 'default'))
1115
+ ) {
1116
+ barrier = 'a `default` re-export'
1117
+ }
1118
+ return
1119
+ }
1120
+ if (typeScript.isImportEqualsDeclaration(node)) {
1121
+ if (
1122
+ typeScript.isExternalModuleReference(node.moduleReference) &&
1123
+ typeScript.isStringLiteralLike(node.moduleReference.expression)
1124
+ ) {
1125
+ barrier = specifierBarrier(node.moduleReference.expression)
1126
+ }
1127
+ return
1128
+ }
1129
+ if (typeScript.isExportAssignment(node)) {
1130
+ if (node.isExportEquals !== true && !insideModuleDeclaration) {
1131
+ barrier = 'an `export default` declaration'
1132
+ }
1133
+ return
1134
+ }
1135
+ if (typeScript.isImportTypeNode(node)) {
1136
+ if (
1137
+ typeScript.isLiteralTypeNode(node.argument) &&
1138
+ typeScript.isStringLiteralLike(node.argument.literal) &&
1139
+ !hasResolutionMode(node.attributes)
1140
+ ) {
1141
+ barrier = specifierBarrier(node.argument.literal)
1142
+ }
1143
+ // `import('pkg').T<import('./x.js').U>` nests another specifier in the
1144
+ // type arguments — keep descending.
1145
+ if (barrier === undefined) {
1146
+ typeScript.forEachChild(node, (child) =>
1147
+ visit(child, insideModuleDeclaration),
1148
+ )
1149
+ }
1150
+ return
1151
+ }
1152
+ if (
1153
+ typeScript.isCallExpression(node) &&
1154
+ node.arguments.length >= 1 &&
1155
+ typeScript.isStringLiteralLike(node.arguments[0]) &&
1156
+ (node.expression.kind === typeScript.SyntaxKind.ImportKeyword ||
1157
+ (typeScript.isIdentifier(node.expression) &&
1158
+ node.expression.text === 'require'))
1159
+ ) {
1160
+ barrier = specifierBarrier(node.arguments[0])
1161
+ return
1162
+ }
1163
+ typeScript.forEachChild(node, (child) =>
1164
+ visit(child, insideModuleDeclaration),
1165
+ )
1166
+ }
1167
+ for (const statement of sourceFile.statements) {
1168
+ visit(statement, false)
1169
+ }
1170
+ if (barrier === undefined) {
1171
+ // `/// <reference types>` resolves through module resolution — a relative
1172
+ // name can diverge the same way a relative specifier can. `path`
1173
+ // references are file-path resolution and carry over honestly.
1174
+ for (const reference of typeScript.preProcessFile(source, true, true)
1175
+ .typeReferenceDirectives) {
1176
+ if (
1177
+ reference.fileName.startsWith('.') &&
1178
+ reference.resolutionMode === undefined
1179
+ ) {
1180
+ barrier = `a relative '/// <reference types="${reference.fileName}" />' directive`
1181
+ break
1182
+ }
1183
+ }
1184
+ }
1185
+ return barrier
1186
+ }
1187
+
974
1188
  function collectRelativeDeclarationSpecifierReferences(
975
1189
  source: string,
976
1190
  ): DeclarationSpecifierReference[] {
@@ -1007,6 +1221,11 @@ function collectRelativeDeclarationSpecifierReferences(
1007
1221
  typeScript.isStringLiteralLike(node.argument.literal)
1008
1222
  ) {
1009
1223
  addStringLiteral(node.argument.literal)
1224
+ } else if (
1225
+ typeScript.isModuleDeclaration(node) &&
1226
+ typeScript.isStringLiteralLike(node.name)
1227
+ ) {
1228
+ addStringLiteral(node.name)
1010
1229
  } else if (
1011
1230
  typeScript.isExternalModuleReference(node) &&
1012
1231
  node.expression &&
@@ -1015,7 +1234,7 @@ function collectRelativeDeclarationSpecifierReferences(
1015
1234
  addStringLiteral(node.expression)
1016
1235
  } else if (
1017
1236
  typeScript.isCallExpression(node) &&
1018
- node.arguments.length === 1 &&
1237
+ node.arguments.length >= 1 &&
1019
1238
  typeScript.isStringLiteralLike(node.arguments[0]) &&
1020
1239
  (node.expression.kind === typeScript.SyntaxKind.ImportKeyword ||
1021
1240
  (typeScript.isIdentifier(node.expression) &&