@aljazmc/testero 0.2.1 → 0.3.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
@@ -12,6 +12,7 @@ testero math unit/ # run test files whose path matches a pattern
12
12
  testero -t "adds numbers" # run tests whose full name matches
13
13
  testero --verbose --bail # list every test, stop at the first failing file
14
14
  testero --listTests # print the test files instead of running them
15
+ testero --config # write the defaults to testero.config.ts, --config js to testero.config.js
15
16
  testero --help
16
17
  ```
17
18
 
package/bin/main.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // shared/config/src/config.ts
4
- import { existsSync, readdirSync, readFileSync } from "node:fs";
4
+ import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
5
5
  import { join } from "node:path";
6
6
  import { pathToFileURL } from "node:url";
7
7
 
@@ -87,6 +87,62 @@ var Config = class _Config {
87
87
  }
88
88
  return new _Config(defaults, root);
89
89
  }
90
+ /** Handles `--config`, writing the defaults to a TypeScript config file, or with `--config js` a JavaScript one. Returns whether it was given. */
91
+ static init(name, defaults, argv = process.argv.slice(2), root = process.cwd()) {
92
+ const index = argv.indexOf("--config");
93
+ if (index === -1) return false;
94
+ const next = argv[index + 1];
95
+ const extension = next === void 0 || next.startsWith("-") ? "ts" : next;
96
+ try {
97
+ console.log(
98
+ `created: ${_Config.export(name, defaults, extension, root)}`
99
+ );
100
+ } catch (error) {
101
+ console.error(error instanceof Error ? error.message : error);
102
+ process.exitCode = 1;
103
+ }
104
+ return true;
105
+ }
106
+ /** Writes the defaults to `<name>.config.<extension>` in the root, unless a config file is there already. */
107
+ static export(name, defaults, extension = "ts", root = process.cwd()) {
108
+ if (!_Config.extensions.includes(extension))
109
+ throw new Error(
110
+ `Unknown config file type ${extension}, use ts or js.`
111
+ );
112
+ for (const existing of _Config.extensions) {
113
+ const file2 = `${name}.config.${existing}`;
114
+ if (existsSync(join(root, file2)))
115
+ throw new Error(`${file2} already exists.`);
116
+ }
117
+ const file = `${name}.config.${extension}`;
118
+ writeFileSync(
119
+ join(root, file),
120
+ `export default ${_Config.#literal(defaults)};
121
+ `
122
+ );
123
+ return file;
124
+ }
125
+ /** A value as JavaScript source, objects one property per line, short arrays on one line. */
126
+ static #literal(value, indent = "") {
127
+ const inner = `${indent} `;
128
+ const lines = (items, open, close) => items.length === 0 ? `${open}${close}` : `${open}
129
+ ${items.map((item) => `${inner}${item},
130
+ `).join("")}${indent}${close}`;
131
+ if (Array.isArray(value)) {
132
+ const items = value.map((item) => _Config.#literal(item, inner));
133
+ const line = `[${items.join(", ")}]`;
134
+ return line.length <= 60 && !line.includes("\n") ? line : lines(items, "[", "]");
135
+ }
136
+ if (value?.constructor === Object)
137
+ return lines(
138
+ Object.entries(value).map(
139
+ ([key, item]) => `${/^[A-Za-z_$][\w$]*$/.test(key) ? key : JSON.stringify(key)}: ${_Config.#literal(item, inner)}`
140
+ ),
141
+ "{",
142
+ "}"
143
+ );
144
+ return JSON.stringify(value);
145
+ }
90
146
  /** Deep merges plain objects, anything else in override replaces base. */
91
147
  static merge(base, override) {
92
148
  const plain = (value) => value?.constructor === Object;
@@ -148,6 +204,7 @@ Options:
148
204
  --listTests print the test files instead of running them
149
205
  --passWithNoTests succeed when there are no test files
150
206
  --testTimeout <ms> milliseconds a test or hook may take
207
+ --config [js] write the default testero.config.ts, or .js
151
208
  -h, --help show this help
152
209
  `;
153
210
  patterns = [];
@@ -1592,7 +1649,7 @@ import { dirname as dirname2, isAbsolute, relative } from "node:path";
1592
1649
  import { fileURLToPath } from "node:url";
1593
1650
 
1594
1651
  // packages/testero/src/transformer.ts
1595
- import { mkdtempSync, readFileSync as readFileSync2, rmSync, writeFileSync } from "node:fs";
1652
+ import { mkdtempSync, readFileSync as readFileSync2, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
1596
1653
  import { tmpdir } from "node:os";
1597
1654
  import { dirname, extname, join as join2, sep } from "node:path";
1598
1655
  import { pathToFileURL as pathToFileURL2 } from "node:url";
@@ -1610,6 +1667,8 @@ var Transformer = class _Transformer {
1610
1667
  ".mts": "ts",
1611
1668
  ".tsx": "tsx"
1612
1669
  };
1670
+ /** The import.meta properties that describe the source file. */
1671
+ static #meta = ["url", "filename", "dirname"];
1613
1672
  /** Marks the resolutions the plugin asks esbuild for itself. */
1614
1673
  static #own = /* @__PURE__ */ Symbol("testero");
1615
1674
  /** Where compiled test files go, stack frames there are testero's own. */
@@ -1618,7 +1677,7 @@ var Transformer = class _Transformer {
1618
1677
  #globals = join2(this.#directory, "globals.mjs");
1619
1678
  #count = 0;
1620
1679
  constructor() {
1621
- writeFileSync(this.#globals, Globals.source());
1680
+ writeFileSync2(this.#globals, Globals.source());
1622
1681
  }
1623
1682
  /** Bundles a test file, returns the URL of the module to import. */
1624
1683
  async compile(file) {
@@ -1634,6 +1693,12 @@ var Transformer = class _Transformer {
1634
1693
  sourcemap: "inline",
1635
1694
  sourcesContent: false,
1636
1695
  inject: [this.#globals],
1696
+ define: Object.fromEntries(
1697
+ _Transformer.#meta.map((name) => [
1698
+ `import.meta.${name}`,
1699
+ `__testero.${name}`
1700
+ ])
1701
+ ),
1637
1702
  // CommonJS code in the bundle may require installed packages
1638
1703
  banner: {
1639
1704
  js: 'import { createRequire as __testeroRequire } from "node:module";\nconst require = __testeroRequire(import.meta.url);'
@@ -1641,7 +1706,7 @@ var Transformer = class _Transformer {
1641
1706
  logLevel: "silent",
1642
1707
  plugins: [this.#plugin()]
1643
1708
  });
1644
- writeFileSync(outfile, outputFiles[0]?.text ?? "");
1709
+ writeFileSync2(outfile, outputFiles[0]?.text ?? "");
1645
1710
  return pathToFileURL2(outfile).href;
1646
1711
  }
1647
1712
  /** Removes the compiled files. */
@@ -1652,6 +1717,21 @@ var Transformer = class _Transformer {
1652
1717
  return {
1653
1718
  name: "testero",
1654
1719
  setup: (build2) => {
1720
+ build2.onResolve({ filter: /^testero-meta:/ }, (args) => ({
1721
+ path: args.path.slice("testero-meta:".length),
1722
+ namespace: "testero-meta"
1723
+ }));
1724
+ build2.onLoad(
1725
+ { filter: /.*/, namespace: "testero-meta" },
1726
+ (args) => ({
1727
+ contents: `export default ${JSON.stringify({
1728
+ url: pathToFileURL2(args.path).href,
1729
+ filename: args.path,
1730
+ dirname: dirname(args.path)
1731
+ })};`,
1732
+ loader: "js"
1733
+ })
1734
+ );
1655
1735
  build2.onResolve({ filter: /^[^./]/ }, async (args) => {
1656
1736
  if (args.pluginData === _Transformer.#own) return void 0;
1657
1737
  if (_Transformer.modules.includes(args.path))
@@ -1674,15 +1754,10 @@ var Transformer = class _Transformer {
1674
1754
  });
1675
1755
  build2.onLoad({ filter: /\.[cm]?[jt]sx?$/ }, (args) => {
1676
1756
  if (_Transformer.#installed(args.path)) return void 0;
1677
- const values = {
1678
- url: pathToFileURL2(args.path).href,
1679
- filename: args.path,
1680
- dirname: dirname(args.path)
1681
- };
1682
- const contents = readFileSync2(args.path, "utf8").replace(
1683
- /\bimport\.meta\.(url|filename|dirname)\b/g,
1684
- (_, name) => JSON.stringify(values[name])
1685
- );
1757
+ const source = readFileSync2(args.path, "utf8");
1758
+ const contents = /\bimport\.meta\b/.test(source) ? `${source}
1759
+ import __testero from ${JSON.stringify(`testero-meta:${args.path}`)};
1760
+ ` : source;
1686
1761
  return {
1687
1762
  contents,
1688
1763
  loader: _Transformer.#loaders[extname(args.path)] ?? "js",
@@ -2125,6 +2200,7 @@ var Testero = class {
2125
2200
 
2126
2201
  // packages/testero/src/index.ts
2127
2202
  try {
2203
+ if (Config.init("testero", config_default)) process.exit();
2128
2204
  const args = new Arguments(process.argv.slice(2));
2129
2205
  if (args.help) process.stdout.write(Arguments.usage);
2130
2206
  else {
@@ -2,7 +2,7 @@ import type { Rules } from "./rules.js";
2
2
  import type { Options } from "./testero.js";
3
3
  /** Command line arguments, named like Jest's. */
4
4
  export default class Arguments {
5
- static readonly usage = "Usage: testero [patterns...] [options]\n\nRuns the test files whose paths match a pattern, every test file without patterns.\n\nOptions:\n -t, --testNamePattern <regex> run only tests whose name matches\n --bail stop after the first failing test file\n --verbose list every test\n --listTests print the test files instead of running them\n --passWithNoTests succeed when there are no test files\n --testTimeout <ms> milliseconds a test or hook may take\n -h, --help show this help\n";
5
+ static readonly usage = "Usage: testero [patterns...] [options]\n\nRuns the test files whose paths match a pattern, every test file without patterns.\n\nOptions:\n -t, --testNamePattern <regex> run only tests whose name matches\n --bail stop after the first failing test file\n --verbose list every test\n --listTests print the test files instead of running them\n --passWithNoTests succeed when there are no test files\n --testTimeout <ms> milliseconds a test or hook may take\n --config [js] write the default testero.config.ts, or .js\n -h, --help show this help\n";
6
6
  readonly patterns: string[];
7
7
  readonly rules: Partial<Rules>;
8
8
  namePattern: RegExp | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aljazmc/testero",
3
- "version": "0.2.1",
3
+ "version": "0.3.1",
4
4
  "description": "A simple, Jest like test runner. Easy to understand, easy to customize and easy to extend.",
5
5
  "author": "aljazmc",
6
6
  "license": "MIT",
@@ -20,7 +20,7 @@
20
20
  "modular"
21
21
  ],
22
22
  "engines": {
23
- "node": "^20.19.0 || ^22.13.0 || ^24 || ^25 || ^26"
23
+ "node": "^22.18.0 || ^24 || ^25 || ^26"
24
24
  },
25
25
  "types": "./bin/types/testero.d.ts",
26
26
  "bin": "./bin/main.js",