@knighted/duel 1.0.0-rc.9 → 1.0.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 CHANGED
@@ -21,7 +21,7 @@ Node.js tool for building a TypeScript dual package.
21
21
  First, install this package to create the `duel` executable inside your `node_modules/.bin` directory.
22
22
 
23
23
  ```console
24
- user@comp ~ $ npm i @knighted/duel
24
+ user@comp ~ $ npm i @knighted/duel --save-dev
25
25
  ```
26
26
 
27
27
  Then, given a `package.json` that defines `"type": "module"` and a `tsconfig.json` file that looks something like the following:
@@ -86,7 +86,7 @@ The available options are limited, because you should define most of them inside
86
86
 
87
87
  * `--project, -p` The path to the project's configuration file. Defaults to `tsconfig.json`.
88
88
  * `--pkg-dir, -k` The directory to start looking for a package.json file. Defaults to the cwd.
89
- * `--dirs, -d` Outputs both builds to directories inside of `outDir`. Defalts to `false`.
89
+ * `--dirs, -d` Outputs both builds to directories inside of `outDir`. Defaults to `false`.
90
90
  * `--parallel, -l` Run the builds in parallel. Defaults to `false`.
91
91
 
92
92
  You can run `duel --help` to get the same info. Below is the output of that:
@@ -106,7 +106,7 @@ Options:
106
106
 
107
107
  These are definitely edge cases, and would only really come up if your project mixes file extensions. For example, if you have `.ts` files combined with `.mts`, and/or `.cts`. For most projects, things should just work as expected.
108
108
 
109
- * This is going to work best if your CJS-first project uses file extensions in their _relative_ specifiers. This is completely acceptable in CJS projects, and [required in ESM projects](https://nodejs.org/api/esm.html#import-specifiers). This package makes no attempt to rewrite bare specifiers, or remap any relative specifiers to a directory index.
109
+ * This is going to work best if your CJS-first project uses file extensions in _relative_ specifiers. This is completely acceptable in CJS projects, and [required in ESM projects](https://nodejs.org/api/esm.html#import-specifiers). This package makes no attempt to rewrite bare specifiers, or remap any relative specifiers to a directory index.
110
110
 
111
111
  * Unfortunately, TypeScript doesn't really build [dual packages](https://nodejs.org/api/packages.html#dual-commonjses-module-packages) very well in regards to preserving module system by file extension. For instance, there doesn't appear to be a way to convert an arbitrary `.ts` file into another module system, _while also preserving the module system of `.mts` and `.cts` files_, without requiring **multiple** package.json files. In my opinion, the `tsc` compiler is fundamentally broken in this regard, and at best is enforcing usage patterns it shouldn't. This is only mentioned for transparency, `duel` will correct for this and produce files with the module system you would expect based on the file's extension, so that it works with [how Node.js determines module systems](https://nodejs.org/api/packages.html#determining-module-system).
112
112
 
package/dist/duel.cjs CHANGED
@@ -12,10 +12,16 @@ var _promises = require("node:fs/promises");
12
12
  var _nodeCrypto = require("node:crypto");
13
13
  var _nodePerf_hooks = require("node:perf_hooks");
14
14
  var _glob = require("glob");
15
+ var _findUp = require("find-up");
15
16
  var _specifier = require("@knighted/specifier");
16
17
  var _init = require("./init.cjs");
17
18
  var _util = require("./util.cjs");
18
- const tsc = (0, _nodePath.join)((0, _nodeProcess.cwd)(), 'node_modules', '.bin', 'tsc');
19
+ const tsc = await (0, _findUp.findUp)(async dir => {
20
+ const tscBin = (0, _nodePath.join)(dir, 'node_modules', '.bin', 'tsc');
21
+ if (await (0, _findUp.pathExists)(tscBin)) {
22
+ return tscBin;
23
+ }
24
+ });
19
25
  const runBuild = (project, outDir) => {
20
26
  return new Promise((resolve, reject) => {
21
27
  const args = outDir ? ['-p', project, '--outDir', outDir] : ['-p', project];
@@ -71,21 +77,24 @@ const duel = async args => {
71
77
  for (const filename of filenames) {
72
78
  const dts = /(\.d\.ts)$/;
73
79
  const outFilename = dts.test(filename) ? filename.replace(dts, isCjsBuild ? '.d.cts' : '.d.mts') : filename.replace(/\.js$/, targetExt);
74
- const code = await _specifier.specifier.update(filename, ({
80
+ const {
81
+ code,
82
+ error
83
+ } = await _specifier.specifier.update(filename, ({
75
84
  value
76
85
  }) => {
77
- // Collapse any BinaryExpression or NewExpression to test for a relative specifier
78
86
  const collapsed = value.replace(/['"`+)\s]|new String\(/g, '');
79
87
  const relative = /^(?:\.|\.\.)\//;
80
88
  if (relative.test(collapsed)) {
81
- // $2 is for any closing quotation/parens around BE or NE
82
89
  return value.replace(/(.+)\.js([)'"`]*)?$/, `$1${targetExt}$2`);
83
90
  }
84
91
  });
85
- await (0, _promises.writeFile)(outFilename, code);
86
- await (0, _promises.rm)(filename, {
87
- force: true
88
- });
92
+ if (code && !error) {
93
+ await (0, _promises.writeFile)(outFilename, code);
94
+ await (0, _promises.rm)(filename, {
95
+ force: true
96
+ });
97
+ }
89
98
  }
90
99
  };
91
100
  const logSuccess = start => {
@@ -114,13 +123,7 @@ const duel = async args => {
114
123
  const prepStart = _nodePerf_hooks.performance.now();
115
124
  await (0, _promises.cp)(projectDir, paraTempDir, {
116
125
  recursive: true,
117
- /**
118
- * Ignore common .gitignored directories in Node.js projects.
119
- * Except node_modules.
120
- *
121
- * @see https://github.com/github/gitignore/blob/main/Node.gitignore
122
- */
123
- filter: src => !/logs|pids|lib-cov|coverage|bower_components|build|dist|jspm_packages|web_modules|out|\.next|\.tsbuildinfo|\.npm|\.node_repl_history|\.tgz|\.yarn|\.pnp|\.nyc_output|\.grunt/i.test(src)
126
+ filter: src => !/logs|pids|lib-cov|coverage|bower_components|build|dist|jspm_packages|web_modules|out|\.next|\.tsbuildinfo|\.npm|\.node_repl_history|\.tgz|\.yarn|\.pnp|\.nyc_output|\.grunt|\.DS_Store/i.test(src)
124
127
  });
125
128
  const dualConfigPath = (0, _nodePath.join)(paraTempDir, 'tsconfig.json');
126
129
  const absoluteDualOutDir = (0, _nodePath.join)(paraTempDir, isCjsBuild ? (0, _nodePath.join)(outDir, 'cjs') : (0, _nodePath.join)(outDir, 'esm'));
@@ -146,7 +149,6 @@ const duel = async args => {
146
149
  ignore: 'node_modules/**'
147
150
  });
148
151
  await updateSpecifiersAndFileExtensions(filenames);
149
- // Copy over and cleanup
150
152
  await (0, _promises.cp)(absoluteDualOutDir, (0, _nodePath.join)(absoluteOutDir, isCjsBuild ? 'cjs' : 'esm'), {
151
153
  recursive: true
152
154
  });
@@ -173,21 +175,11 @@ const duel = async args => {
173
175
  const absoluteDualOutDir = (0, _nodePath.join)(projectDir, isCjsBuild ? (0, _nodePath.join)(outDir, 'cjs') : (0, _nodePath.join)(outDir, 'esm'));
174
176
  const tsconfigDual = getOverrideTsConfig();
175
177
  const pkgRename = 'package.json.bak';
176
-
177
- /**
178
- * Create a new package.json with updated `type` field.
179
- * Create a new tsconfig.json.
180
- *
181
- * The need to create a new package.json makes doing
182
- * the builds in parallel difficult.
183
- */
184
178
  await (0, _promises.rename)(pkg.path, (0, _nodePath.join)(pkgDir, pkgRename));
185
179
  await (0, _promises.writeFile)(pkg.path, JSON.stringify({
186
180
  type: isCjsBuild ? 'commonjs' : 'module'
187
181
  }));
188
182
  await (0, _promises.writeFile)(dualConfigPath, JSON.stringify(tsconfigDual));
189
-
190
- // Build dual
191
183
  (0, _util.log)('Starting dual build...');
192
184
  try {
193
185
  await runBuild(dualConfigPath, absoluteDualOutDir);
@@ -197,8 +189,6 @@ const duel = async args => {
197
189
  success = false;
198
190
  (0, _util.logError)(message);
199
191
  }
200
-
201
- // Cleanup and restore
202
192
  await (0, _promises.rm)(dualConfigPath, {
203
193
  force: true
204
194
  });
package/dist/duel.js CHANGED
@@ -1,15 +1,21 @@
1
1
  #!/usr/bin/env node
2
- import { argv, cwd } from 'node:process';
2
+ import { argv } from 'node:process';
3
3
  import { join, dirname, resolve } from 'node:path';
4
4
  import { spawn } from 'node:child_process';
5
5
  import { writeFile, rm, cp, rename, stat, access, constants } from 'node:fs/promises';
6
6
  import { randomBytes } from 'node:crypto';
7
7
  import { performance } from 'node:perf_hooks';
8
8
  import { glob } from 'glob';
9
+ import { findUp, pathExists } from 'find-up';
9
10
  import { specifier } from '@knighted/specifier';
10
11
  import { init } from './init.js';
11
12
  import { getRealPathAsFileUrl, logError, log } from './util.js';
12
- const tsc = join(cwd(), 'node_modules', '.bin', 'tsc');
13
+ const tsc = await findUp(async dir => {
14
+ const tscBin = join(dir, 'node_modules', '.bin', 'tsc');
15
+ if (await pathExists(tscBin)) {
16
+ return tscBin;
17
+ }
18
+ });
13
19
  const runBuild = (project, outDir) => {
14
20
  return new Promise((resolve, reject) => {
15
21
  const args = outDir ? ['-p', project, '--outDir', outDir] : ['-p', project];
@@ -65,21 +71,24 @@ const duel = async args => {
65
71
  for (const filename of filenames) {
66
72
  const dts = /(\.d\.ts)$/;
67
73
  const outFilename = dts.test(filename) ? filename.replace(dts, isCjsBuild ? '.d.cts' : '.d.mts') : filename.replace(/\.js$/, targetExt);
68
- const code = await specifier.update(filename, ({
74
+ const {
75
+ code,
76
+ error
77
+ } = await specifier.update(filename, ({
69
78
  value
70
79
  }) => {
71
- // Collapse any BinaryExpression or NewExpression to test for a relative specifier
72
80
  const collapsed = value.replace(/['"`+)\s]|new String\(/g, '');
73
81
  const relative = /^(?:\.|\.\.)\//;
74
82
  if (relative.test(collapsed)) {
75
- // $2 is for any closing quotation/parens around BE or NE
76
83
  return value.replace(/(.+)\.js([)'"`]*)?$/, `$1${targetExt}$2`);
77
84
  }
78
85
  });
79
- await writeFile(outFilename, code);
80
- await rm(filename, {
81
- force: true
82
- });
86
+ if (code && !error) {
87
+ await writeFile(outFilename, code);
88
+ await rm(filename, {
89
+ force: true
90
+ });
91
+ }
83
92
  }
84
93
  };
85
94
  const logSuccess = start => {
@@ -108,13 +117,7 @@ const duel = async args => {
108
117
  const prepStart = performance.now();
109
118
  await cp(projectDir, paraTempDir, {
110
119
  recursive: true,
111
- /**
112
- * Ignore common .gitignored directories in Node.js projects.
113
- * Except node_modules.
114
- *
115
- * @see https://github.com/github/gitignore/blob/main/Node.gitignore
116
- */
117
- filter: src => !/logs|pids|lib-cov|coverage|bower_components|build|dist|jspm_packages|web_modules|out|\.next|\.tsbuildinfo|\.npm|\.node_repl_history|\.tgz|\.yarn|\.pnp|\.nyc_output|\.grunt/i.test(src)
120
+ filter: src => !/logs|pids|lib-cov|coverage|bower_components|build|dist|jspm_packages|web_modules|out|\.next|\.tsbuildinfo|\.npm|\.node_repl_history|\.tgz|\.yarn|\.pnp|\.nyc_output|\.grunt|\.DS_Store/i.test(src)
118
121
  });
119
122
  const dualConfigPath = join(paraTempDir, 'tsconfig.json');
120
123
  const absoluteDualOutDir = join(paraTempDir, isCjsBuild ? join(outDir, 'cjs') : join(outDir, 'esm'));
@@ -140,7 +143,6 @@ const duel = async args => {
140
143
  ignore: 'node_modules/**'
141
144
  });
142
145
  await updateSpecifiersAndFileExtensions(filenames);
143
- // Copy over and cleanup
144
146
  await cp(absoluteDualOutDir, join(absoluteOutDir, isCjsBuild ? 'cjs' : 'esm'), {
145
147
  recursive: true
146
148
  });
@@ -167,21 +169,11 @@ const duel = async args => {
167
169
  const absoluteDualOutDir = join(projectDir, isCjsBuild ? join(outDir, 'cjs') : join(outDir, 'esm'));
168
170
  const tsconfigDual = getOverrideTsConfig();
169
171
  const pkgRename = 'package.json.bak';
170
-
171
- /**
172
- * Create a new package.json with updated `type` field.
173
- * Create a new tsconfig.json.
174
- *
175
- * The need to create a new package.json makes doing
176
- * the builds in parallel difficult.
177
- */
178
172
  await rename(pkg.path, join(pkgDir, pkgRename));
179
173
  await writeFile(pkg.path, JSON.stringify({
180
174
  type: isCjsBuild ? 'commonjs' : 'module'
181
175
  }));
182
176
  await writeFile(dualConfigPath, JSON.stringify(tsconfigDual));
183
-
184
- // Build dual
185
177
  log('Starting dual build...');
186
178
  try {
187
179
  await runBuild(dualConfigPath, absoluteDualOutDir);
@@ -191,8 +183,6 @@ const duel = async args => {
191
183
  success = false;
192
184
  logError(message);
193
185
  }
194
-
195
- // Cleanup and restore
196
186
  await rm(dualConfigPath, {
197
187
  force: true
198
188
  });
package/dist/init.cjs CHANGED
@@ -8,8 +8,10 @@ var _nodeProcess = require("node:process");
8
8
  var _nodeUtil = require("node:util");
9
9
  var _nodePath = require("node:path");
10
10
  var _promises = require("node:fs/promises");
11
+ var _stripJsonComments = _interopRequireDefault(require("strip-json-comments"));
11
12
  var _readPkgUp = require("read-pkg-up");
12
13
  var _util = require("./util.cjs");
14
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
13
15
  const init = async args => {
14
16
  let parsed = null;
15
17
  try {
@@ -103,7 +105,7 @@ const init = async args => {
103
105
  if (stats.isFile()) {
104
106
  let tsconfig = null;
105
107
  try {
106
- tsconfig = JSON.parse((await (0, _promises.readFile)(configPath)).toString());
108
+ tsconfig = JSON.parse((0, _stripJsonComments.default)((await (0, _promises.readFile)(configPath)).toString()));
107
109
  } catch (err) {
108
110
  (0, _util.logError)(`The config file found at ${configPath} is not parsable as JSON.`);
109
111
  return false;
package/dist/init.js CHANGED
@@ -2,6 +2,7 @@ import { cwd } from 'node:process';
2
2
  import { parseArgs } from 'node:util';
3
3
  import { resolve, join, dirname } from 'node:path';
4
4
  import { stat, readFile } from 'node:fs/promises';
5
+ import stripJsonComments from 'strip-json-comments';
5
6
  import { readPackageUp } from 'read-pkg-up';
6
7
  import { logError, log } from './util.js';
7
8
  const init = async args => {
@@ -97,7 +98,7 @@ const init = async args => {
97
98
  if (stats.isFile()) {
98
99
  let tsconfig = null;
99
100
  try {
100
- tsconfig = JSON.parse((await readFile(configPath)).toString());
101
+ tsconfig = JSON.parse(stripJsonComments((await readFile(configPath)).toString()));
101
102
  } catch (err) {
102
103
  logError(`The config file found at ${configPath} is not parsable as JSON.`);
103
104
  return false;
package/dist/util.cjs CHANGED
@@ -7,7 +7,6 @@ exports.logError = exports.log = exports.getRealPathAsFileUrl = void 0;
7
7
  var _nodeUrl = require("node:url");
8
8
  var _promises = require("node:fs/promises");
9
9
  const log = (color = '\x1b[30m', msg = '') => {
10
- // eslint-disable-next-line no-console
11
10
  console.log(`${color}%s\x1b[0m`, msg);
12
11
  };
13
12
  exports.log = log;
package/dist/util.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import { pathToFileURL } from 'node:url';
2
2
  import { realpath } from 'node:fs/promises';
3
3
  const log = (color = '\x1b[30m', msg = '') => {
4
- // eslint-disable-next-line no-console
5
4
  console.log(`${color}%s\x1b[0m`, msg);
6
5
  };
7
6
  const logError = log.bind(null, '\x1b[31m');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knighted/duel",
3
- "version": "1.0.0-rc.9",
3
+ "version": "1.0.1",
4
4
  "description": "TypeScript dual packages.",
5
5
  "type": "module",
6
6
  "main": "dist",
@@ -21,7 +21,7 @@
21
21
  "prettier": "prettier -w src/*.js test/*.js",
22
22
  "lint": "eslint src/*.js test/*.js",
23
23
  "test": "c8 --reporter=text --reporter=text-summary --reporter=lcov node --test --test-reporter=spec test/*.js",
24
- "build": "babel-dual-package --no-cjs-dir src",
24
+ "build": "babel-dual-package --no-cjs-dir --no-comments src",
25
25
  "prepack": "npm run build"
26
26
  },
27
27
  "keywords": [
@@ -44,22 +44,24 @@
44
44
  "url": "https://github.com/knightedcodemonkey/duel/issues"
45
45
  },
46
46
  "peerDependencies": {
47
- "typescript": ">=4.0.0 || >=4.8.0-dev || >=4.9.0-dev || >=5.0.0-dev || >=5.1.0-dev || >=5.2.0-dev"
47
+ "typescript": ">=4.0.0 || >=4.9.0-dev || >=5.2.0-dev || >=5.3.0-dev"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@types/node": "^20.4.6",
51
- "babel-dual-package": "^1.0.0",
51
+ "babel-dual-package": "^1.1.2",
52
52
  "c8": "^8.0.1",
53
53
  "eslint": "^8.45.0",
54
54
  "eslint-plugin-n": "^16.0.1",
55
55
  "prettier": "^3.0.1",
56
- "typescript": "^5.2.0-dev.20230727",
56
+ "typescript": "^5.3.0-dev.20230906",
57
57
  "vite": "^4.4.8"
58
58
  },
59
59
  "dependencies": {
60
- "@knighted/specifier": "^1.0.0-rc.1",
60
+ "@knighted/specifier": "^1.0.1",
61
+ "find-up": "^6.3.0",
61
62
  "glob": "^10.3.3",
62
- "read-pkg-up": "^10.0.0"
63
+ "read-pkg-up": "^10.0.0",
64
+ "strip-json-comments": "^5.0.1"
63
65
  },
64
66
  "prettier": {
65
67
  "arrowParens": "avoid",