Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: privatenumber/tsx
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v3.12.9
Choose a base ref
...
head repository: privatenumber/tsx
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v3.12.10
Choose a head ref
  • 2 commits
  • 6 files changed
  • 1 contributor

Commits on Sep 13, 2023

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    84d722d View commit details
  2. Copy the full SHA
    ccde465 View commit details
Showing with 135 additions and 119 deletions.
  1. +1 −1 .nvmrc
  2. +2 −1 package.json
  3. +11 −4 pnpm-lock.yaml
  4. +9 −0 release.config.cjs
  5. +111 −113 tests/specs/watch.ts
  6. +1 −0 tests/utils/tsx.ts
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v18.13.0
v20.6.1
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -47,7 +47,7 @@
"dependencies": {
"@esbuild-kit/cjs-loader": "^2.4.2",
"@esbuild-kit/core-utils": "^3.3.0",
"@esbuild-kit/esm-loader": "^2.6.1"
"@esbuild-kit/esm-loader": "^2.6.3"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
@@ -70,6 +70,7 @@
"lint-staged": "^13.1.0",
"manten": "^1.1.0",
"node-pty": "^1.0.0",
"outdent": "^0.8.0",
"pkgroll": "^1.8.0",
"semver": "^7.3.8",
"simple-git-hooks": "^2.8.1",
15 changes: 11 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions release.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const outdent = require('outdent');
module.exports = {
addReleases: 'bottom',
successComment: outdent`
:tada: This issue has been resolved in v\${nextRelease.version}
If you appreciate this project, please consider [supporting this project by sponsoring](https://github.com/sponsors/privatenumber) :heart: :pray:
`,
};
224 changes: 111 additions & 113 deletions tests/specs/watch.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,32 @@
import { type Readable } from 'node:stream';
import path from 'path';
import { setTimeout } from 'timers/promises';
import { on } from 'events';
import { testSuite, expect } from 'manten';
import { createFixture } from 'fs-fixture';
import { tsx } from '../utils/tsx';

type MaybePromise<T> = T | Promise<T>;
const interact = async (
stdout: Readable,
actions: ((data: string) => MaybePromise<boolean | void>)[],
) => {
let currentAction = actions.shift();

const buffers: Buffer[] = [];
while (currentAction) {
for await (const [chunk] of on(stdout, 'data')) {
buffers.push(chunk);
if (await currentAction(chunk.toString())) {
currentAction = actions.shift();
break;
}
}
}

return Buffer.concat(buffers).toString();
};

export default testSuite(async ({ describe }, fixturePath: string) => {
describe('watch', ({ test, describe }) => {
test('require file path', async () => {
@@ -15,10 +38,17 @@ export default testSuite(async ({ describe }, fixturePath: string) => {
});

test('watch files for changes', async ({ onTestFinish }) => {
let initialValue = Date.now();
const fixture = await createFixture({
'index.js': 'console.log(1)',
'package.json': JSON.stringify({
type: 'module',
}),
'index.js': `
import { value } from './value.js';
console.log(value);
`,
'value.js': `export const value = ${initialValue};`,
});

onTestFinish(async () => await fixture.rm());

const tsxProcess = tsx({
@@ -28,21 +58,23 @@ export default testSuite(async ({ describe }, fixturePath: string) => {
],
});

await new Promise<void>((resolve) => {
async function onStdOut(data: Buffer) {
const chunkString = data.toString();
await interact(
tsxProcess.stdout!,
[
async (data) => {
if (data.includes(`${initialValue}\n`)) {
initialValue = Date.now();
await fixture.writeFile('value.js', `export const value = ${initialValue};`);
return true;
}
},
data => data.includes(`${initialValue}\n`),
],
);

if (chunkString.match('1\n')) {
await fixture.writeFile('index.js', 'console.log(2)');
} else if (chunkString.match('2\n')) {
tsxProcess.kill();
resolve();
}
}
tsxProcess.kill();

tsxProcess.stdout!.on('data', onStdOut);
tsxProcess.stderr!.on('data', onStdOut);
});
await tsxProcess;
}, 10_000);

test('suppresses warnings & clear screen', async () => {
@@ -53,32 +85,24 @@ export default testSuite(async ({ describe }, fixturePath: string) => {
],
});

const stdout = await new Promise<string>((resolve) => {
let aggregateStdout = '';
let hitEnter = false;

function onStdOut(data: Buffer) {
const chunkString = data.toString();
// console.log({ chunkString });

aggregateStdout += chunkString;

if (chunkString.match('log-argv.ts')) {
if (hitEnter) {
tsxProcess.kill();
resolve(aggregateStdout);
} else {
hitEnter = true;
await interact(
tsxProcess.stdout!,
[
(data) => {
if (data.includes('log-argv.ts')) {
tsxProcess.stdin?.write('enter');
return true;
}
}
}
tsxProcess.stdout!.on('data', onStdOut);
tsxProcess.stderr!.on('data', onStdOut);
});
},
data => data.includes('log-argv.ts'),
],
);

tsxProcess.kill();

expect(stdout).not.toMatch('Warning');
expect(stdout).toMatch('\u001Bc');
const { all } = await tsxProcess;
expect(all).not.toMatch('Warning');
expect(all).toMatch('\u001Bc');
}, 10_000);

test('passes flags', async () => {
@@ -90,20 +114,15 @@ export default testSuite(async ({ describe }, fixturePath: string) => {
],
});

const stdout = await new Promise<string>((resolve) => {
tsxProcess.stdout!.on('data', (chunk) => {
const chunkString = chunk.toString();
if (chunkString.startsWith('[')) {
resolve(chunkString);
}
});
});
await interact(
tsxProcess.stdout!,
[data => data.startsWith('["')],
);

tsxProcess.kill();

expect(stdout).toMatch('"--some-flag"');

await tsxProcess;
const { all } = await tsxProcess;
expect(all).toMatch('"--some-flag"');
}, 10_000);

test('wait for exit', async ({ onTestFinish }) => {
@@ -130,42 +149,23 @@ export default testSuite(async ({ describe }, fixturePath: string) => {
],
});

const stdout = await new Promise<string>((resolve) => {
const buffers: Buffer[] = [];
const waitingOn: [string, (() => void)][] = [
['start\n', () => {
tsxProcess.stdin?.write('enter');
}],
['end\n', () => {}],
];

let currentWaitingOn = waitingOn.shift();
async function onStdOut(data: Buffer) {
buffers.push(data);
const chunkString = data.toString();

if (currentWaitingOn) {
const [expected, callback] = currentWaitingOn!;

// eslint-disable-next-line unicorn/prefer-regexp-test
if (chunkString.match(expected)) {
callback();
currentWaitingOn = waitingOn.shift();
if (!currentWaitingOn) {
tsxProcess.kill();
resolve(Buffer.concat(buffers).toString());
}
await interact(
tsxProcess.stdout!,
[
(data) => {
if (data.includes('start\n')) {
tsxProcess.stdin?.write('enter');
return true;
}
}
}

tsxProcess.stdout!.on('data', onStdOut);
tsxProcess.stderr!.on('data', onStdOut);
});
},
data => data.includes('end\n'),
],
);

expect(stdout).toMatch(/start[\s\S]+end/);
tsxProcess.kill();

await tsxProcess;
const { all } = await tsxProcess;
expect(all).toMatch(/start[\s\S]+end/);
}, 10_000);

describe('help', ({ test }) => {
@@ -188,20 +188,15 @@ export default testSuite(async ({ describe }, fixturePath: string) => {
],
});

const stdout = await new Promise<string>((resolve) => {
tsxProcess.stdout!.on('data', (chunk) => {
const chunkString = chunk.toString();
if (chunkString.startsWith('[')) {
resolve(chunkString);
}
});
});
await interact(
tsxProcess.stdout!,
[data => data.startsWith('["')],
);

tsxProcess.kill();

expect(stdout).toMatch('"--help"');

await tsxProcess;
const { all } = await tsxProcess;
expect(all).toMatch('"--help"');
}, 5000);
});

@@ -235,28 +230,31 @@ export default testSuite(async ({ describe }, fixturePath: string) => {
],
});

tsxProcess.stdout!.on('data', async (data: Buffer) => {
const chunkString = data.toString();
if (chunkString === `${value} ${value}\n`) {
value = Date.now();
await Promise.all([
fixture.writeFile(fileA, `export default ${value}`),
fixture.writeFile(fileB, `export default ${value}`),
]);

await setTimeout(500);
await fixture.writeFile(entryFile, 'console.log("TERMINATE")');
}

if (chunkString === 'TERMINATE\n') {
tsxProcess.kill();
}
});
await interact(
tsxProcess.stdout!,
[
async (data) => {
if (data === `${value} ${value}\n`) {
value = Date.now();
await Promise.all([
fixture.writeFile(fileA, `export default ${value}`),
fixture.writeFile(fileB, `export default ${value}`),
]);

await setTimeout(500);
await fixture.writeFile(entryFile, 'console.log("TERMINATE")');
return true;
}
},
data => data === 'TERMINATE\n',
],
);

const tsxProcessResolved = await tsxProcess;
tsxProcess.kill();

expect(tsxProcessResolved.stdout).not.toMatch(`${value} ${value}`);
expect(tsxProcessResolved.stderr).toBe('');
const { all, stderr } = await tsxProcess;
expect(all).not.toMatch(`${value} ${value}`);
expect(stderr).toBe('');
}, 10_000);
});
});
1 change: 1 addition & 0 deletions tests/utils/tsx.ts
Original file line number Diff line number Diff line change
@@ -25,6 +25,7 @@ export const tsx = (
nodeOptions: [],
cwd: options.cwd,
reject: false,
all: true,
},
);