@nimblescape/typescript 0.2.0 → 0.2.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.
- package/README.md +46 -11
- package/dist/ts.js +187 -85
- package/package.json +1 -1
- package/types/nimblescape-globals/index.d.ts +2 -2
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Node.js, in one step: `ts main.ts`. A program can use `prompt()`, `alert()`
|
|
|
6
6
|
and `confirm()` as in the browser, without an import. A folder needs no
|
|
7
7
|
`tsconfig.json` and no `package.json`.
|
|
8
8
|
|
|
9
|
-

|
|
10
10
|
|
|
11
11
|
`ts` is a small wrapper around `tsc` and `node`. Every error comes from
|
|
12
12
|
`tsc` or from Node.js, and the only addition to Node.js is the three
|
|
@@ -37,14 +37,14 @@ comes first in these terminals.
|
|
|
37
37
|
ts # runs main.ts of this folder
|
|
38
38
|
ts dice.ts 3 6 # runs dice.ts with the arguments 3 and 6
|
|
39
39
|
ts --check main.ts # checks main.ts and runs nothing
|
|
40
|
+
ts main.js # runs a JavaScript file in the same way
|
|
40
41
|
```
|
|
41
42
|
|
|
42
43
|
| Command | Effect |
|
|
43
44
|
| --- | --- |
|
|
44
45
|
| `ts [file] [arguments]` | Checks the file with `tsc`, compiles it and runs it with Node.js. Without a file, `ts` runs `main.ts` of the current folder. The words after the file are the arguments of the program (`process.argv`). When `tsc` finds an error, `ts` prints it and runs nothing. |
|
|
45
46
|
| `ts --check [file]` | Checks the file and runs nothing. Without a file, `ts` checks `main.ts` of the current folder, and without `main.ts` the project of the `tsconfig.json` of the current folder. |
|
|
46
|
-
| `ts --init` | Writes a `tsconfig.json` into the current folder: the file of `tsc --init`, with the settings for Node.js (`lib` `esnext
|
|
47
|
-
| `ts --init --browser` | Writes a `tsconfig.json` for code that runs in the browser: `lib` `esnext` and `dom`, and no types of Node.js. |
|
|
47
|
+
| `ts --init` | Writes a `tsconfig.json` into the current folder: the file of `tsc --init`, with the settings for Node.js (`lib` `esnext`, the types of Node.js and `rewriteRelativeImportExtensions`), with the entry `"plugins": [{ "name": "nimblescape-typescript-plugin" }]`, which marks a project of `ts` (see [Projects with a tsconfig.json](#projects-with-a-tsconfigjson)), and without `noUncheckedIndexedAccess`. With TypeScript 7, it also writes a `package.json` with `"type": "module"` when the folder and the folders above have none (see [Limits](#limits)); with TypeScript 6, `ts` and the editor need none. When the folder cannot find the types of Node.js, `ts` asks to run `npm install --save-dev @types/node`. It replaces no existing file. |
|
|
48
48
|
| `ts --install-typescript` | Installs the TypeScript of the newest VS Code globally with `npm install --global typescript@<version>`, without a question. |
|
|
49
49
|
| `ts --completion <shell>` | Prints the Tab completion script for `bash`, `zsh` or `fish` (see [Tab completion](#tab-completion)). |
|
|
50
50
|
| `ts --version`, `ts -v` | Prints the versions of `ts`, of the TypeScript that `ts` uses and of Node.js. |
|
|
@@ -53,6 +53,11 @@ ts --check main.ts # checks main.ts and runs nothing
|
|
|
53
53
|
The options stand before the file; every word after the file goes to the
|
|
54
54
|
program, also a word that starts with `-`. `--` ends the options.
|
|
55
55
|
|
|
56
|
+
A JavaScript file (`.js`, `.mjs` or `.cjs`) runs in the same way, also
|
|
57
|
+
when a `tsconfig.json` does not allow JavaScript. `tsc` checks its syntax,
|
|
58
|
+
and its types only when the file starts with the comment `// @ts-check`,
|
|
59
|
+
as in Deno. A TypeScript file may import a JavaScript file.
|
|
60
|
+
|
|
56
61
|
`ts` ends with the exit code of the program, or with the exit code of `tsc`
|
|
57
62
|
after an error of the check. An error of the program names the line of the
|
|
58
63
|
`.ts` file, because Node.js runs with the source maps of `tsc`.
|
|
@@ -69,9 +74,17 @@ after an error of the check. An error of the program names the line of the
|
|
|
69
74
|
|
|
70
75
|
They read from the keyboard and wait, so the program stops until you press
|
|
71
76
|
Enter. Input from a file or a pipe works too, for example
|
|
72
|
-
`ts main.ts < input.txt`.
|
|
73
|
-
|
|
74
|
-
|
|
77
|
+
`ts main.ts < input.txt`.
|
|
78
|
+
|
|
79
|
+
`ts` knows their types in every project of `ts`, as Deno knows its own
|
|
80
|
+
globals: the types are part of `ts`, and a project needs no file for them.
|
|
81
|
+
In a project of `ts --init`, `ts` adds them to the settings of its
|
|
82
|
+
`tsconfig.json`, unless the project has the types of the browser (`"lib"`
|
|
83
|
+
with `"dom"`), which declare the three functions themselves. The VS Code extension
|
|
84
|
+
[nimblescape TypeScript](https://marketplace.visualstudio.com/items?itemName=nimblescape.nimblescape-typescript)
|
|
85
|
+
does the same in the editor. Plain `tsc` does not know them; a project that
|
|
86
|
+
also has to pass plain `tsc` declares them in a file of its own, for example
|
|
87
|
+
`globals.d.ts`:
|
|
75
88
|
|
|
76
89
|
```ts
|
|
77
90
|
declare function prompt(message?: string, defaultValue?: string): string | null;
|
|
@@ -91,12 +104,31 @@ wins. Without one, `ts` uses the built-in defaults of `tsc` (for example
|
|
|
91
104
|
| `lib` | `["esnext"]` | JavaScript without the browser: `document` and `window` are errors. |
|
|
92
105
|
| `types` | `["node"]` and the types of `prompt()`, `alert()` and `confirm()` | `process`, `node:fs` and the other modules of Node.js. `ts` contains the types of Node.js. |
|
|
93
106
|
| `skipLibCheck` | `true` | Checks your files, not the type files of the packages, as `tsc --init` sets it. |
|
|
107
|
+
| `allowJs` | `true` | `ts` runs a JavaScript file, and a TypeScript file may import one. `checkJs` stays off, as in `tsc` and Deno. |
|
|
94
108
|
| `rewriteRelativeImportExtensions` | `true` | An import names the `.ts` file, as Node.js needs it: `import { Dog } from "./dog.ts"`. |
|
|
95
109
|
|
|
96
110
|
Without a `package.json`, every file is an ES module, as if the folder had a
|
|
97
|
-
`package.json` with `"type": "module"
|
|
111
|
+
`package.json` with `"type": "module"`, as in Deno. This also holds for a
|
|
112
|
+
project of `ts --init`, in `ts` and in the editor; plain `tsc` reads such a
|
|
113
|
+
file as CommonJS for `module` `nodenext`. With a `package.json`, its `type`
|
|
98
114
|
decides, as in Node.js.
|
|
99
115
|
|
|
116
|
+
## Projects with a tsconfig.json
|
|
117
|
+
|
|
118
|
+
A `tsconfig.json` belongs to `ts` only when its `compilerOptions.plugins`
|
|
119
|
+
names the editor plugin, as `ts --init` writes it:
|
|
120
|
+
|
|
121
|
+
```json
|
|
122
|
+
"plugins": [{ "name": "nimblescape-typescript-plugin" }]
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Only such a project gets the types of `prompt()`, `alert()` and `confirm()`,
|
|
126
|
+
and every file of it is an ES module without a `package.json`. `tsc` and
|
|
127
|
+
Vite ignore the entry. Any other `tsconfig.json`, for example the one of a
|
|
128
|
+
Vite project, is a standard project: `ts` checks it as `tsc` does, and the
|
|
129
|
+
editor plugin leaves it to TypeScript. Delete the line, and the project of
|
|
130
|
+
`ts --init` becomes a standard project.
|
|
131
|
+
|
|
100
132
|
## Which TypeScript
|
|
101
133
|
|
|
102
134
|
`ts` contains no TypeScript. It uses the first of these:
|
|
@@ -149,11 +181,14 @@ bash-completion, and for zsh when `~/.zshrc` runs `compinit`.
|
|
|
149
181
|
- TypeScript 7 cannot read the `package.json` that `ts` assumes: without a
|
|
150
182
|
`package.json`, `ts` checks a file there with `module` `preserve` and
|
|
151
183
|
`moduleResolution` `bundler`. The program runs the same.
|
|
152
|
-
- With a `tsconfig.json`, TypeScript 7 does not add the declaration files of
|
|
153
|
-
the project (`.d.ts`) to the check of one file; TypeScript 6 does.
|
|
154
|
-
- After `ts --init`, install the types of Node.js in the project:
|
|
155
|
-
`npm install --save-dev @types/node`.
|
|
156
184
|
- `ts` runs no JSX and builds no libraries.
|
|
185
|
+
- Plain `tsc` does not know `prompt()`, `alert()` and `confirm()`; see
|
|
186
|
+
[the functions prompt, alert and confirm](#the-functions-prompt-alert-and-confirm).
|
|
187
|
+
It also does not make every file an ES module without a `package.json`.
|
|
188
|
+
- TypeScript 7 cannot read the `package.json` that `ts` assumes in a
|
|
189
|
+
project with a `tsconfig.json` either: there, the project needs a real
|
|
190
|
+
`package.json` with `"type": "module"`, which `ts --init` writes with
|
|
191
|
+
TypeScript 7.
|
|
157
192
|
|
|
158
193
|
---
|
|
159
194
|
|
package/dist/ts.js
CHANGED
|
@@ -33,7 +33,6 @@ var SHELLS = ["bash", "zsh", "fish"];
|
|
|
33
33
|
var OPTIONS = [
|
|
34
34
|
{ name: "--check", description: "check the types only and run nothing" },
|
|
35
35
|
{ name: "--init", description: "write a tsconfig.json with the settings of tsc --init for Node.js" },
|
|
36
|
-
{ name: "--browser", description: "with --init, the settings for code that runs in the browser" },
|
|
37
36
|
{ name: "--install-typescript", description: "install the TypeScript of the newest VS Code globally, without a question" },
|
|
38
37
|
{ name: "--completion", argument: "<shell>", values: SHELLS, description: "print the Tab completion script for bash, zsh or fish" },
|
|
39
38
|
{ name: "--version", short: "-v", description: "print the versions of ts, TypeScript and Node.js" },
|
|
@@ -43,7 +42,7 @@ function helpText(version) {
|
|
|
43
42
|
const usage = [
|
|
44
43
|
["ts [file] [arguments]", "check, compile and run the file with Node.js (default main.ts)"],
|
|
45
44
|
["ts --check [file]", "check the file, or the project of the tsconfig.json of this folder"],
|
|
46
|
-
["ts --init
|
|
45
|
+
["ts --init", "write a tsconfig.json into this folder"],
|
|
47
46
|
["ts --install-typescript", "install TypeScript globally with npm"],
|
|
48
47
|
["ts --completion <shell>", "print the Tab completion script"]
|
|
49
48
|
];
|
|
@@ -56,7 +55,7 @@ function helpText(version) {
|
|
|
56
55
|
return rows.map(([left, right]) => ` ${left.padEnd(width)} ${right}`).join("\n");
|
|
57
56
|
};
|
|
58
57
|
return [
|
|
59
|
-
`ts ${version}: checks a TypeScript file with tsc, compiles it and runs it with Node.js.`,
|
|
58
|
+
`ts ${version}: checks a TypeScript or JavaScript file with tsc, compiles it and runs it with Node.js.`,
|
|
60
59
|
"",
|
|
61
60
|
"Usage:",
|
|
62
61
|
column(usage),
|
|
@@ -72,7 +71,7 @@ function helpText(version) {
|
|
|
72
71
|
|
|
73
72
|
// src/cli/args.ts
|
|
74
73
|
function parseArguments(argv) {
|
|
75
|
-
const command = { check: false, init: false,
|
|
74
|
+
const command = { check: false, init: false, installTypeScript: false, version: false, help: false, args: [] };
|
|
76
75
|
const known = new Set(OPTIONS.flatMap((option) => [option.name, option.short].filter((name) => name !== void 0)));
|
|
77
76
|
for (let i = 0; i < argv.length; i++) {
|
|
78
77
|
const word = argv[i];
|
|
@@ -89,7 +88,6 @@ function parseArguments(argv) {
|
|
|
89
88
|
if (!known.has(word)) return `Unknown option ${word}. See ts --help.`;
|
|
90
89
|
if (word === "--check") command.check = true;
|
|
91
90
|
else if (word === "--init") command.init = true;
|
|
92
|
-
else if (word === "--browser") command.browser = true;
|
|
93
91
|
else if (word === "--install-typescript") command.installTypeScript = true;
|
|
94
92
|
else if (word === "--version" || word === "-v") command.version = true;
|
|
95
93
|
else if (word === "--help" || word === "-h") command.help = true;
|
|
@@ -98,7 +96,6 @@ function parseArguments(argv) {
|
|
|
98
96
|
if (!SHELLS.includes(command.completion)) return `--completion needs one of: ${SHELLS.join(", ")}.`;
|
|
99
97
|
}
|
|
100
98
|
}
|
|
101
|
-
if (command.browser && !command.init) return "--browser works only with --init.";
|
|
102
99
|
return command;
|
|
103
100
|
}
|
|
104
101
|
|
|
@@ -109,15 +106,27 @@ var path3 = __toESM(require("node:path"));
|
|
|
109
106
|
|
|
110
107
|
// src/defaults.ts
|
|
111
108
|
var path = __toESM(require("node:path"));
|
|
109
|
+
var PLUGIN_NAME = "nimblescape-typescript-plugin";
|
|
110
|
+
function isTsProject(plugins) {
|
|
111
|
+
return Array.isArray(plugins) && plugins.some((entry) => typeof entry === "object" && entry !== null && entry.name === PLUGIN_NAME);
|
|
112
|
+
}
|
|
112
113
|
var GLOBALS_TYPES = "nimblescape-globals";
|
|
113
114
|
var DEFAULT_COMPILER_OPTIONS = {
|
|
114
115
|
module: "nodenext",
|
|
115
116
|
lib: ["esnext"],
|
|
116
117
|
types: ["node", GLOBALS_TYPES],
|
|
117
118
|
skipLibCheck: true,
|
|
119
|
+
// ts runs a JavaScript file too, and a TypeScript file may import one.
|
|
120
|
+
allowJs: true,
|
|
118
121
|
// A relative import may end with .ts, as Node.js requires for a .ts file; tsc writes .js into the output.
|
|
119
122
|
rewriteRelativeImportExtensions: true
|
|
120
123
|
};
|
|
124
|
+
function hasBrowserTypes(lib) {
|
|
125
|
+
return lib === void 0 || lib.some((name) => /^(lib\.)?dom(\.|$)/i.test(name));
|
|
126
|
+
}
|
|
127
|
+
function isJavaScriptFile(file) {
|
|
128
|
+
return /\.[cm]?jsx?$/i.test(file);
|
|
129
|
+
}
|
|
121
130
|
var VIRTUAL_PACKAGE_JSON = '{"type":"module"}';
|
|
122
131
|
function rootPackageJson(file) {
|
|
123
132
|
return path.join(path.parse(path.resolve(file)).root, "package.json");
|
|
@@ -243,16 +252,23 @@ function packageRoot(environment = process.env) {
|
|
|
243
252
|
return environment[TYPES_VARIABLE] || path3.resolve(__dirname, "..");
|
|
244
253
|
}
|
|
245
254
|
function typeRoots(folder, environment = process.env) {
|
|
255
|
+
const roots = projectTypeRoots(folder);
|
|
256
|
+
const own = packageRoot(environment);
|
|
257
|
+
roots.push(path3.join(own, "types"));
|
|
258
|
+
const node = packageFolder("@types/node", own);
|
|
259
|
+
if (node !== void 0 && !roots.includes(path3.dirname(node))) roots.push(path3.dirname(node));
|
|
260
|
+
return roots;
|
|
261
|
+
}
|
|
262
|
+
function globalsFile(environment = process.env) {
|
|
263
|
+
return path3.join(packageRoot(environment), "types", GLOBALS_TYPES, "index.d.ts");
|
|
264
|
+
}
|
|
265
|
+
function projectTypeRoots(folder) {
|
|
246
266
|
const roots = [];
|
|
247
267
|
for (let current = path3.resolve(folder); ; current = path3.dirname(current)) {
|
|
248
268
|
const types = path3.join(current, "node_modules", "@types");
|
|
249
269
|
if (fs2.existsSync(types)) roots.push(types);
|
|
250
270
|
if (path3.dirname(current) === current) break;
|
|
251
271
|
}
|
|
252
|
-
const own = packageRoot(environment);
|
|
253
|
-
roots.push(path3.join(own, "types"));
|
|
254
|
-
const node = packageFolder("@types/node", own);
|
|
255
|
-
if (node !== void 0 && !roots.includes(path3.dirname(node))) roots.push(path3.dirname(node));
|
|
256
272
|
return roots;
|
|
257
273
|
}
|
|
258
274
|
function projectOfFile(file) {
|
|
@@ -260,7 +276,7 @@ function projectOfFile(file) {
|
|
|
260
276
|
const folder = path3.dirname(absolute);
|
|
261
277
|
const configFolder = findUp(folder, "tsconfig.json", fs2.existsSync);
|
|
262
278
|
const root = findUp(folder, "package.json", fs2.existsSync);
|
|
263
|
-
const type = root === void 0 ? "module" :
|
|
279
|
+
const type = root !== void 0 ? readManifest(root)?.type : configFolder === void 0 ? "module" : void 0;
|
|
264
280
|
return {
|
|
265
281
|
file: absolute,
|
|
266
282
|
config: configFolder === void 0 ? void 0 : path3.join(configFolder, "tsconfig.json"),
|
|
@@ -268,11 +284,42 @@ function projectOfFile(file) {
|
|
|
268
284
|
moduleType: type === "module" || type === "commonjs" ? type : void 0
|
|
269
285
|
};
|
|
270
286
|
}
|
|
271
|
-
function
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
const
|
|
275
|
-
|
|
287
|
+
function projectConfig(install2, typescript, config) {
|
|
288
|
+
const isDeclaration = (name) => /\.d\.[cm]?ts$/.test(name);
|
|
289
|
+
const strings = (value) => Array.isArray(value) ? value.filter((name) => typeof name === "string") : void 0;
|
|
290
|
+
const settings = (files, typeRoots2, lib, plugins) => ({
|
|
291
|
+
files,
|
|
292
|
+
declarations: files.filter(isDeclaration),
|
|
293
|
+
typeRoots: typeRoots2,
|
|
294
|
+
lib,
|
|
295
|
+
ts: isTsProject(plugins)
|
|
296
|
+
});
|
|
297
|
+
const broken = settings([], false, void 0, void 0);
|
|
298
|
+
if (typescript) {
|
|
299
|
+
const read = typescript.readConfigFile(config, typescript.sys.readFile);
|
|
300
|
+
if (read.error) return broken;
|
|
301
|
+
const parsed = typescript.parseJsonConfigFileContent(read.config, typescript.sys, path3.dirname(config), void 0, config);
|
|
302
|
+
return settings(parsed.fileNames, parsed.options.typeRoots !== void 0, parsed.options.lib, parsed.options.plugins);
|
|
303
|
+
}
|
|
304
|
+
const manifest = readManifest(install2.folder);
|
|
305
|
+
const bin = path3.join(install2.folder, manifest?.bin?.tsc ?? "bin/tsc");
|
|
306
|
+
const result = (0, import_node_child_process2.spawnSync)(process.execPath, [bin, "--showConfig", "--project", config], { encoding: "utf8" });
|
|
307
|
+
try {
|
|
308
|
+
const shown = JSON.parse(result.stdout);
|
|
309
|
+
const files = (strings(shown.files) ?? []).map((name) => path3.resolve(path3.dirname(config), name));
|
|
310
|
+
return settings(files, shown.compilerOptions?.typeRoots !== void 0, strings(shown.compilerOptions?.lib), shown.compilerOptions?.plugins);
|
|
311
|
+
} catch {
|
|
312
|
+
return broken;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
function addedFiles(settings) {
|
|
316
|
+
return !settings.ts || hasBrowserTypes(settings.lib) ? [] : [globalsFile()];
|
|
317
|
+
}
|
|
318
|
+
function extendedTypeRoots(config, folder, settings) {
|
|
319
|
+
const own = projectTypeRoots(path3.dirname(config));
|
|
320
|
+
const found = projectTypeRoots(folder);
|
|
321
|
+
const missing = !settings.typeRoots && own.some((root) => !found.includes(root));
|
|
322
|
+
return missing ? { typeRoots: [.../* @__PURE__ */ new Set([...own, ...found])] } : {};
|
|
276
323
|
}
|
|
277
324
|
function loadTypeScript(install2) {
|
|
278
325
|
if (install2.major >= 7) return void 0;
|
|
@@ -285,7 +332,10 @@ function outputFile(job) {
|
|
|
285
332
|
const relative3 = path3.relative(path3.parse(file).root, file).replace(/\.([cm]?)tsx?$/, (_, kind) => `.${kind}js`);
|
|
286
333
|
return path3.join(outFolder(job), relative3);
|
|
287
334
|
}
|
|
288
|
-
function
|
|
335
|
+
function jobSettings(job, typescript) {
|
|
336
|
+
return job.project.config === void 0 ? void 0 : projectConfig(job.typescript, typescript, job.project.config);
|
|
337
|
+
}
|
|
338
|
+
function temporaryConfig(job, typescript, settings = jobSettings(job, typescript)) {
|
|
289
339
|
const { project } = job;
|
|
290
340
|
const output = job.checkOnly ? { noEmit: true } : {
|
|
291
341
|
noEmit: false,
|
|
@@ -300,12 +350,13 @@ function temporaryConfig(job, typescript) {
|
|
|
300
350
|
composite: false,
|
|
301
351
|
incremental: false
|
|
302
352
|
};
|
|
303
|
-
if (project.config !== void 0) {
|
|
304
|
-
const
|
|
353
|
+
if (project.config !== void 0 && settings !== void 0) {
|
|
354
|
+
const roots = extendedTypeRoots(project.config, job.folder, settings);
|
|
305
355
|
return {
|
|
306
356
|
extends: project.config,
|
|
307
|
-
|
|
308
|
-
|
|
357
|
+
// A JavaScript file runs also when the tsconfig.json does not allow JavaScript.
|
|
358
|
+
compilerOptions: { ...output, ...roots, rewriteRelativeImportExtensions: true, ...isJavaScriptFile(project.file) ? { allowJs: true } : {} },
|
|
359
|
+
files: [.../* @__PURE__ */ new Set([project.file, ...settings.declarations, ...addedFiles(settings)])],
|
|
309
360
|
include: []
|
|
310
361
|
};
|
|
311
362
|
}
|
|
@@ -339,9 +390,10 @@ function runTsc(install2, typescript, args, file) {
|
|
|
339
390
|
}
|
|
340
391
|
function compile(job) {
|
|
341
392
|
const typescript = loadTypeScript(job.typescript);
|
|
393
|
+
const settings = jobSettings(job, typescript);
|
|
342
394
|
fs2.mkdirSync(job.folder, { recursive: true });
|
|
343
395
|
const config = path3.join(job.folder, "tsconfig.json");
|
|
344
|
-
fs2.writeFileSync(config, `${JSON.stringify(temporaryConfig(job, typescript), null, 2)}
|
|
396
|
+
fs2.writeFileSync(config, `${JSON.stringify(temporaryConfig(job, typescript, settings), null, 2)}
|
|
345
397
|
`);
|
|
346
398
|
if (!job.checkOnly) {
|
|
347
399
|
fs2.mkdirSync(outFolder(job), { recursive: true });
|
|
@@ -349,15 +401,27 @@ function compile(job) {
|
|
|
349
401
|
if (type !== void 0) fs2.writeFileSync(path3.join(outFolder(job), "package.json"), `${JSON.stringify({ type })}
|
|
350
402
|
`);
|
|
351
403
|
}
|
|
352
|
-
const virtual = job.project.
|
|
404
|
+
const virtual = job.project.packageRoot === void 0 && (settings === void 0 || settings.ts);
|
|
353
405
|
return runTsc(job.typescript, typescript, ["--project", config], virtual ? job.project.file : void 0);
|
|
354
406
|
}
|
|
355
|
-
function checkProject(install2, config) {
|
|
407
|
+
function checkProject(install2, config, folder) {
|
|
356
408
|
const typescript = loadTypeScript(install2);
|
|
357
|
-
const settings =
|
|
358
|
-
const
|
|
359
|
-
const
|
|
360
|
-
return runTsc(install2, typescript,
|
|
409
|
+
const settings = projectConfig(install2, typescript, config);
|
|
410
|
+
const virtual = settings.ts && findUp(path3.dirname(config), "package.json", fs2.existsSync) === void 0 ? config : void 0;
|
|
411
|
+
const references = parseJsonc(fs2.readFileSync(config, "utf8"))?.references;
|
|
412
|
+
if (Array.isArray(references) && references.length > 0) return runTsc(install2, typescript, ["--build", config], virtual);
|
|
413
|
+
const added = addedFiles(settings);
|
|
414
|
+
if (added.length === 0) return runTsc(install2, typescript, ["--project", config, "--noEmit"], virtual);
|
|
415
|
+
const temporary = path3.join(folder, "tsconfig.json");
|
|
416
|
+
const json = {
|
|
417
|
+
extends: config,
|
|
418
|
+
compilerOptions: { noEmit: true, ...extendedTypeRoots(config, folder, settings) },
|
|
419
|
+
files: [.../* @__PURE__ */ new Set([...settings.files, ...added])],
|
|
420
|
+
include: []
|
|
421
|
+
};
|
|
422
|
+
fs2.writeFileSync(temporary, `${JSON.stringify(json, null, 2)}
|
|
423
|
+
`);
|
|
424
|
+
return runTsc(install2, typescript, ["--project", temporary], virtual);
|
|
361
425
|
}
|
|
362
426
|
|
|
363
427
|
// src/cli/completion.ts
|
|
@@ -430,6 +494,46 @@ var import_node_child_process3 = require("node:child_process");
|
|
|
430
494
|
var fs3 = __toESM(require("node:fs"));
|
|
431
495
|
var os = __toESM(require("node:os"));
|
|
432
496
|
var path4 = __toESM(require("node:path"));
|
|
497
|
+
|
|
498
|
+
// src/cli/ask.ts
|
|
499
|
+
var import_node_fs = require("node:fs");
|
|
500
|
+
var import_node_tty = require("node:tty");
|
|
501
|
+
function canAsk() {
|
|
502
|
+
return (0, import_node_tty.isatty)(0) && (0, import_node_tty.isatty)(1);
|
|
503
|
+
}
|
|
504
|
+
function readLine() {
|
|
505
|
+
const bytes = [];
|
|
506
|
+
const buffer = new Uint8Array(1);
|
|
507
|
+
for (; ; ) {
|
|
508
|
+
let count;
|
|
509
|
+
try {
|
|
510
|
+
count = (0, import_node_fs.readSync)(0, buffer, 0, 1, null);
|
|
511
|
+
} catch (error) {
|
|
512
|
+
if (error.code === "EAGAIN") {
|
|
513
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 20);
|
|
514
|
+
continue;
|
|
515
|
+
}
|
|
516
|
+
throw error;
|
|
517
|
+
}
|
|
518
|
+
if (count !== 1) break;
|
|
519
|
+
if (buffer[0] === 10) return new TextDecoder().decode(new Uint8Array(bytes)).replace(/\r$/, "");
|
|
520
|
+
bytes.push(buffer[0]);
|
|
521
|
+
}
|
|
522
|
+
return bytes.length > 0 ? new TextDecoder().decode(new Uint8Array(bytes)) : null;
|
|
523
|
+
}
|
|
524
|
+
function askYesNo(question) {
|
|
525
|
+
(0, import_node_fs.writeSync)(1, `${question} [Y/n] `);
|
|
526
|
+
const answer = (readLine() ?? "n").trim().toLowerCase();
|
|
527
|
+
return answer === "" || answer === "y" || answer === "yes";
|
|
528
|
+
}
|
|
529
|
+
function askText(question, fallback) {
|
|
530
|
+
(0, import_node_fs.writeSync)(1, `${question} [${fallback}] `);
|
|
531
|
+
const answer = (readLine() ?? "").trim();
|
|
532
|
+
return answer === "" ? fallback : answer;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// src/cli/init.ts
|
|
536
|
+
var NODE_TYPES_COMMAND = ["npm", "install", "--save-dev", "@types/node"];
|
|
433
537
|
function tscInit(install2) {
|
|
434
538
|
const typescript = loadTypeScript(install2);
|
|
435
539
|
if (!typescript) {
|
|
@@ -461,7 +565,7 @@ function tscInit(install2) {
|
|
|
461
565
|
}, ["--init"]);
|
|
462
566
|
return { text, output, code };
|
|
463
567
|
}
|
|
464
|
-
function adapt(text
|
|
568
|
+
function adapt(text) {
|
|
465
569
|
const lines = text.split("\n");
|
|
466
570
|
const removed = /^\s*"(noUncheckedIndexedAccess|declaration|declarationMap|jsx)":/;
|
|
467
571
|
const out = [];
|
|
@@ -476,11 +580,16 @@ function adapt(text, browser) {
|
|
|
476
580
|
break;
|
|
477
581
|
}
|
|
478
582
|
}
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
583
|
+
out.push(
|
|
584
|
+
`${indent}// For Node.js (ts --init), with npm install -D @types/node:`,
|
|
585
|
+
`${indent}"lib": ["esnext"],`,
|
|
586
|
+
`${indent}"types": ["node"],`,
|
|
587
|
+
`${indent}// An import names the .ts file, as Node.js needs it; tsc writes .js:`,
|
|
588
|
+
`${indent}"rewriteRelativeImportExtensions": true,`,
|
|
589
|
+
`${indent}// A project of ts: ts and the editor know prompt(), alert() and confirm(),`,
|
|
590
|
+
`${indent}// and every file is an ES module:`,
|
|
591
|
+
`${indent}"plugins": [{ "name": "${PLUGIN_NAME}" }],`
|
|
592
|
+
);
|
|
484
593
|
while (i + 1 < lines.length && /^\s*\/\/ ("lib"|"types"|and npm install)/.test(lines[i + 1])) i++;
|
|
485
594
|
continue;
|
|
486
595
|
}
|
|
@@ -488,7 +597,26 @@ function adapt(text, browser) {
|
|
|
488
597
|
}
|
|
489
598
|
return out.join("\n");
|
|
490
599
|
}
|
|
491
|
-
function
|
|
600
|
+
function writePackageJson() {
|
|
601
|
+
if (findUp(process.cwd(), "package.json", fs3.existsSync) !== void 0) return void 0;
|
|
602
|
+
fs3.writeFileSync(path4.join(process.cwd(), "package.json"), `${JSON.stringify({ type: "module" }, null, 2)}
|
|
603
|
+
`);
|
|
604
|
+
return 'Created a new package.json with "type": "module", so every file is an ES module';
|
|
605
|
+
}
|
|
606
|
+
function ensureNodeTypes() {
|
|
607
|
+
if (packageFolder("@types/node", process.cwd()) !== void 0) return 0;
|
|
608
|
+
const command = NODE_TYPES_COMMAND.join(" ");
|
|
609
|
+
if (!canAsk()) {
|
|
610
|
+
console.log(`The types of Node.js (@types/node) are not installed. Run ${command} in this folder.`);
|
|
611
|
+
return 0;
|
|
612
|
+
}
|
|
613
|
+
console.log("The types of Node.js (@types/node) are not installed.");
|
|
614
|
+
if (!askYesNo(`Run ${command} now?`)) return 0;
|
|
615
|
+
console.log(`> ${command}`);
|
|
616
|
+
const [npm, ...args] = NODE_TYPES_COMMAND;
|
|
617
|
+
return (0, import_node_child_process3.spawnSync)(npm, args, { stdio: "inherit" }).status ?? 1;
|
|
618
|
+
}
|
|
619
|
+
function init(install2) {
|
|
492
620
|
const target = path4.join(process.cwd(), "tsconfig.json");
|
|
493
621
|
if (fs3.existsSync(target)) {
|
|
494
622
|
console.error(`error TS5054: A 'tsconfig.json' file is already defined at: '${target}'.`);
|
|
@@ -499,52 +627,18 @@ function init(install2, browser) {
|
|
|
499
627
|
process.stdout.write(result.output);
|
|
500
628
|
return result.code || 1;
|
|
501
629
|
}
|
|
502
|
-
fs3.writeFileSync(target, adapt(result.text
|
|
503
|
-
|
|
504
|
-
|
|
630
|
+
fs3.writeFileSync(target, adapt(result.text));
|
|
631
|
+
const line = install2.major >= 7 ? writePackageJson() : void 0;
|
|
632
|
+
const created = /^.*Created a new tsconfig\.json.*$/m;
|
|
633
|
+
if (line === void 0) process.stdout.write(result.output);
|
|
634
|
+
else process.stdout.write(created.test(result.output) ? result.output.replace(created, (text) => `${text}
|
|
635
|
+
${line}`) : `${result.output}${line}
|
|
636
|
+
`);
|
|
637
|
+
return ensureNodeTypes();
|
|
505
638
|
}
|
|
506
639
|
|
|
507
640
|
// src/cli/install.ts
|
|
508
641
|
var import_node_child_process4 = require("node:child_process");
|
|
509
|
-
|
|
510
|
-
// src/cli/ask.ts
|
|
511
|
-
var import_node_fs = require("node:fs");
|
|
512
|
-
var import_node_tty = require("node:tty");
|
|
513
|
-
function canAsk() {
|
|
514
|
-
return (0, import_node_tty.isatty)(0) && (0, import_node_tty.isatty)(1);
|
|
515
|
-
}
|
|
516
|
-
function readLine() {
|
|
517
|
-
const bytes = [];
|
|
518
|
-
const buffer = new Uint8Array(1);
|
|
519
|
-
for (; ; ) {
|
|
520
|
-
let count;
|
|
521
|
-
try {
|
|
522
|
-
count = (0, import_node_fs.readSync)(0, buffer, 0, 1, null);
|
|
523
|
-
} catch (error) {
|
|
524
|
-
if (error.code === "EAGAIN") {
|
|
525
|
-
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 20);
|
|
526
|
-
continue;
|
|
527
|
-
}
|
|
528
|
-
throw error;
|
|
529
|
-
}
|
|
530
|
-
if (count !== 1) break;
|
|
531
|
-
if (buffer[0] === 10) return new TextDecoder().decode(new Uint8Array(bytes)).replace(/\r$/, "");
|
|
532
|
-
bytes.push(buffer[0]);
|
|
533
|
-
}
|
|
534
|
-
return bytes.length > 0 ? new TextDecoder().decode(new Uint8Array(bytes)) : null;
|
|
535
|
-
}
|
|
536
|
-
function askYesNo(question) {
|
|
537
|
-
(0, import_node_fs.writeSync)(1, `${question} [Y/n] `);
|
|
538
|
-
const answer = (readLine() ?? "n").trim().toLowerCase();
|
|
539
|
-
return answer === "" || answer === "y" || answer === "yes";
|
|
540
|
-
}
|
|
541
|
-
function askText(question, fallback) {
|
|
542
|
-
(0, import_node_fs.writeSync)(1, `${question} [${fallback}] `);
|
|
543
|
-
const answer = (readLine() ?? "").trim();
|
|
544
|
-
return answer === "" ? fallback : answer;
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
// src/cli/install.ts
|
|
548
642
|
var TIMEOUT = 4e3;
|
|
549
643
|
async function fetchJson(url2, headers = {}) {
|
|
550
644
|
const response = await fetch(url2, { headers, signal: AbortSignal.timeout(TIMEOUT) });
|
|
@@ -638,14 +732,15 @@ function ensurePackages(folder) {
|
|
|
638
732
|
const missing = missingPackages(folder);
|
|
639
733
|
if (!missing) return true;
|
|
640
734
|
const command = `npm ${installArguments(missing.root).join(" ")}`;
|
|
641
|
-
const
|
|
735
|
+
const relative3 = path5.relative(process.cwd(), missing.root);
|
|
736
|
+
const where = relative3 === "" ? "in this folder" : `in ${relative3}`;
|
|
642
737
|
const names2 = missing.missing.join(", ");
|
|
643
738
|
if (!canAsk()) {
|
|
644
|
-
console.error(`The packages of this course are not installed (${names2}). Run ${command}
|
|
739
|
+
console.error(`The packages of this course are not installed (${names2}). Run ${command} ${where}.`);
|
|
645
740
|
return false;
|
|
646
741
|
}
|
|
647
742
|
console.log(`The packages of this course are not installed (${names2}).`);
|
|
648
|
-
if (!askYesNo(`Run ${command}
|
|
743
|
+
if (!askYesNo(`Run ${command} ${where} now?`)) return false;
|
|
649
744
|
console.log(`> ${command}`);
|
|
650
745
|
const result = (0, import_node_child_process5.spawnSync)("npm", installArguments(missing.root), { cwd: missing.root, stdio: "inherit" });
|
|
651
746
|
return result.status === 0 && missingPackages(folder) === void 0;
|
|
@@ -807,7 +902,7 @@ async function main(argv) {
|
|
|
807
902
|
return 2;
|
|
808
903
|
}
|
|
809
904
|
if (command.help) {
|
|
810
|
-
console.log(helpText("0.2.
|
|
905
|
+
console.log(helpText("0.2.1"));
|
|
811
906
|
return 0;
|
|
812
907
|
}
|
|
813
908
|
if (command.completion !== void 0) {
|
|
@@ -817,7 +912,7 @@ async function main(argv) {
|
|
|
817
912
|
if (command.version) {
|
|
818
913
|
const lookup = findTypeScript(process.cwd());
|
|
819
914
|
const typescript2 = "found" in lookup ? describe(lookup.found) : "TypeScript: not installed";
|
|
820
|
-
console.log(`ts ${"0.2.
|
|
915
|
+
console.log(`ts ${"0.2.1"}
|
|
821
916
|
${typescript2}
|
|
822
917
|
Node.js ${process.version}`);
|
|
823
918
|
return 0;
|
|
@@ -829,17 +924,24 @@ Node.js ${process.version}`);
|
|
|
829
924
|
}
|
|
830
925
|
if (command.init) {
|
|
831
926
|
const typescript2 = await typeScriptFor(process.cwd());
|
|
832
|
-
return typescript2 ? init(typescript2
|
|
927
|
+
return typescript2 ? init(typescript2) : 1;
|
|
833
928
|
}
|
|
834
929
|
const file = entryFile(command.file);
|
|
835
930
|
if (file === void 0) {
|
|
836
931
|
if (command.check && fs6.existsSync("tsconfig.json")) {
|
|
837
932
|
const typescript2 = await typeScriptFor(process.cwd());
|
|
838
|
-
|
|
933
|
+
if (!typescript2) return 1;
|
|
934
|
+
const config = path7.resolve("tsconfig.json");
|
|
935
|
+
const runFolder = createRunFolder(projectOfFile(config));
|
|
936
|
+
try {
|
|
937
|
+
return checkProject(typescript2, config, runFolder.folder);
|
|
938
|
+
} finally {
|
|
939
|
+
removeRunFolder(runFolder);
|
|
940
|
+
}
|
|
839
941
|
}
|
|
840
942
|
console.error(`No ${ENTRY_FILES.join(" or ")} in this folder.
|
|
841
943
|
`);
|
|
842
|
-
console.error(helpText("0.2.
|
|
944
|
+
console.error(helpText("0.2.1"));
|
|
843
945
|
return 1;
|
|
844
946
|
}
|
|
845
947
|
if (!fs6.existsSync(file)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nimblescape/typescript",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "The command ts: checks a TypeScript file with tsc, compiles it and runs it with Node.js, with the global functions prompt(), alert() and confirm().",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
// The types of the polyfills of ts (src/runtime/polyfill.ts): prompt(), alert() and confirm() are global functions in
|
|
6
6
|
// every program that ts runs, as in Deno and the browser. Without a tsconfig.json, ts and the editor plugin add this
|
|
7
|
-
// type package ("types": ["node", "nimblescape-globals"]);
|
|
8
|
-
//
|
|
7
|
+
// type package ("types": ["node", "nimblescape-globals"]); with a tsconfig.json, they add this file to the files of
|
|
8
|
+
// the project, unless the project has the types of the browser (lib dom), which declare the three functions.
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Shows `message`, waits for one line of the keyboard and returns it without the line break. An empty line returns
|