cmpl
Version:
no deps, no operations, no vowels - just a tiny compiler
769 lines (732 loc) • 19.1 kB
text/typescript
/// <reference types="types-node-test" />
import test from 'node:test';
import assert from 'node:assert';
import { relative, join } from 'node:path';
import { cmpl, cntntHsh, wtch, WatchFs, WatchEvent } from './index';
// (async () => {
// for await (const mnfst of wtch({
// entry: __dirname,
// processors: [
// {
// rename: cntntHsh(1),
// outDir: __dirname + '/../dist/test',
// },
// {
// rename: cntntHsh(2),
// outDir: __dirname + '/../dist/test',
// },
// ],
// })) {
// console.log(mnfst);
// }
// })().catch((err) => {
// console.log({ err });
// process.exit(1);
// });
test('cmpl', async (t) => {
await t.test('copies over a single file', async () => {
const fs = createFs(
[
{ method: 'stat', path: 'test.json', isDir: false },
{ method: 'readFile', path: 'test.json', contents: '{"hi":"ho"}' },
{ method: 'mkdir', path: 'dist' },
{
method: 'writeFile',
path: 'dist/test.json',
contents: '{"hi":"ho"}',
},
],
__dirname,
);
assert.deepEqual(
await cmpl({
entry: join(__dirname, 'test.json'),
processors: [
{
outDir: join(__dirname, 'dist'),
},
],
fs,
}),
{ 'test.json': 'test.json' },
);
fs.done();
});
await t.test('copies single file in directory', async () => {
const fs = createFs(
[
{ method: 'stat', path: '', isDir: true },
{ method: 'readdir', path: '', contents: ['test.json'] },
{ method: 'stat', path: 'test.json', isDir: false },
{ method: 'readFile', path: 'test.json', contents: '{"hi":"ho"}' },
{ method: 'mkdir', path: 'dist' },
{
method: 'writeFile',
path: 'dist/test.json',
contents: '{"hi":"ho"}',
},
],
__dirname,
);
assert.deepEqual(
await cmpl({
entry: __dirname,
processors: [
{
outDir: join(__dirname, 'dist'),
},
],
fs,
}),
{ 'test.json': 'test.json' },
);
fs.done();
});
await t.test('copies files in nested directories', async () => {
const fs = createFs(
[
{ method: 'stat', path: '', isDir: true },
{ method: 'readdir', path: '', contents: ['test.json', 'deep'] },
{ method: 'stat', path: 'test.json', isDir: false },
{ method: 'stat', path: 'deep', isDir: true },
{ method: 'readdir', path: 'deep', contents: ['test2.json'] },
{ method: 'stat', path: 'deep/test2.json', isDir: false },
{ method: 'readFile', path: 'test.json', contents: '{"hi":"ho"}' },
{
method: 'readFile',
path: 'deep/test2.json',
contents: '{"hü":"hott"}',
},
{ method: 'mkdir', path: 'dist' },
{
method: 'writeFile',
path: 'dist/test.json',
contents: '{"hi":"ho"}',
},
{ method: 'mkdir', path: 'dist/deep' },
{
method: 'writeFile',
path: 'dist/deep/test2.json',
contents: '{"hü":"hott"}',
},
],
__dirname,
);
assert.deepEqual(
await cmpl({
entry: __dirname,
processors: [
{
outDir: join(__dirname, 'dist'),
},
],
fs,
}),
{
'deep/test2.json': 'deep/test2.json',
'test.json': 'test.json',
},
);
fs.done();
});
await t.test('it ignores nested folders', async () => {
const fs = createFs(
[
{ method: 'stat', path: '', isDir: true },
{ method: 'readdir', path: '', contents: ['test.json', 'deep'] },
{ method: 'stat', path: 'test.json', isDir: false },
{ method: 'stat', path: 'deep', isDir: true },
{ method: 'readFile', path: 'test.json', contents: '{"hi":"ho"}' },
{ method: 'mkdir', path: 'dist' },
{
method: 'writeFile',
path: 'dist/test.json',
contents: '{"hi":"ho"}',
},
],
__dirname,
);
assert.deepEqual(
await cmpl({
entry: __dirname,
processors: [
{
recursive: false,
outDir: join(__dirname, 'dist'),
},
],
fs,
}),
{
'test.json': 'test.json',
},
);
fs.done();
});
await t.test('it ignores specific files', async () => {
const fs = createFs(
[
{ method: 'stat', path: '', isDir: true },
{
method: 'readdir',
path: '',
contents: ['test.json', 'test2.json', 'deep'],
},
{ method: 'stat', path: 'test.json', isDir: false },
{ method: 'stat', path: 'test2.json', isDir: false },
{ method: 'stat', path: 'deep', isDir: true },
{ method: 'readFile', path: 'test.json', contents: '{"hi":"ho"}' },
{ method: 'mkdir', path: 'dist' },
{
method: 'writeFile',
path: 'dist/test.json',
contents: '{"hi":"ho"}',
},
],
__dirname,
);
let i = 0;
assert.deepEqual(
await cmpl({
entry: __dirname,
processors: [
{
include: (name, isDir) => {
switch (i++) {
case 0:
assert.equal(name, 'test.json');
assert.equal(isDir, false);
break;
case 1:
assert.equal(name, 'test2.json');
assert.equal(isDir, false);
break;
case 2:
assert.equal(name, 'deep');
assert.equal(isDir, true);
break;
}
return name === 'test.json';
},
outDir: join(__dirname, 'dist'),
},
],
fs,
}),
{
'test.json': 'test.json',
},
);
fs.done();
});
await t.test('it transforms contents', async () => {
const fs = createFs(
[
{ method: 'stat', path: 'test.json', isDir: false },
{
method: 'readFile',
path: 'test.json',
contents: 'rofl',
},
{ method: 'mkdir', path: 'dist' },
{
method: 'writeFile',
path: 'dist/test.json',
contents: 'leeeeel',
},
],
__dirname,
);
assert.deepEqual(
await cmpl({
entry: join(__dirname, 'test.json'),
processors: [
{
transform: (content, file) => {
assert.equal(file, 'test.json');
assert.equal(content.toString(), 'rofl');
return Buffer.from('leeeeel');
},
outDir: join(__dirname, 'dist'),
},
],
fs,
}),
{
'test.json': 'test.json',
},
);
fs.done();
});
await t.test('it applies content hashes', async () => {
const fs = createFs(
[
{ method: 'stat', path: 'test.json', isDir: false },
{
method: 'readFile',
path: 'test.json',
contents: '{"hi":"ho"}',
},
{ method: 'mkdir', path: 'dist' },
{
method: 'writeFile',
path: 'dist/test-DF67FD3A.json',
contents: '{"hi":"ho"}',
},
],
__dirname,
);
assert.deepEqual(
await cmpl({
entry: join(__dirname, 'test.json'),
processors: [
{
outDir: join(__dirname, 'dist'),
rename: cntntHsh(8),
},
],
fs,
}),
{
'test.json': 'test-DF67FD3A.json',
},
);
fs.done();
});
await t.test('it creates custom file names', async () => {
const fs = createFs(
[
{ method: 'stat', path: 'test.json', isDir: false },
{
method: 'readFile',
path: 'test.json',
contents: '{"hi":"ho"}',
},
{ method: 'mkdir', path: 'dist' },
{
method: 'writeFile',
path: 'dist/q3p45giunrfd.f35r',
contents: '{"hi":"ho"}',
},
],
__dirname,
);
assert.deepEqual(
await cmpl({
entry: join(__dirname, 'test.json'),
processors: [
{
rename: (name, content) => {
assert.equal(name, 'test.json');
assert.equal(content.toString(), '{"hi":"ho"}');
return 'q3p45giunrfd.f35r';
},
outDir: join(__dirname, 'dist'),
},
],
fs,
}),
{
'test.json': 'q3p45giunrfd.f35r',
},
);
fs.done();
});
await t.test('watches single file', async () => {
const watchEvents: Deferred<WatchEvent>[] = [defer()];
const watcher = createWatcher(watchEvents.map(({ promise }) => promise));
const fs = createFs(
[
{ method: 'stat', path: 'test.json', isDir: false },
{
method: 'readFile',
path: 'test.json',
contents: '{"hi":"ho"}',
},
{ method: 'mkdir', path: 'dist' },
{
method: 'writeFile',
path: 'dist/test-DF.json',
contents: '{"hi":"ho"}',
},
{ method: 'stat', path: 'test.json', isDir: false },
{
method: 'watch',
path: 'test.json',
options: { recursive: false, signal: undefined },
watcher,
},
{
method: 'readFile',
path: 'test.json',
contents: '{"ho":"hi"}',
},
{ method: 'mkdir', path: 'dist' },
{
method: 'writeFile',
path: 'dist/test-9E.json',
contents: '{"ho":"hi"}',
},
],
__dirname,
);
const wtchr = wtch({
processors: [
{
outDir: join(__dirname, 'dist'),
rename: cntntHsh(2),
},
],
entry: join(__dirname, 'test.json'),
fs,
});
let i = 0;
for await (const manifest of wtchr) {
switch (i++) {
case 0:
assert.deepEqual(manifest, {
'test.json': 'test-DF.json',
});
watchEvents[0].resolve({
eventType: 'change',
filename: 'test.json',
});
break;
case 1:
assert.deepEqual(manifest, {
'test.json': 'test-9E.json',
});
break;
default:
throw new Error('NEIN');
}
}
fs.done();
});
await t.test('ignores newly added files', async () => {
const watchEvents: Deferred<WatchEvent>[] = [defer()];
const watcher = createWatcher(watchEvents.map(({ promise }) => promise));
const fs = createFs(
[
{ method: 'stat', path: '', isDir: true },
{ method: 'readdir', path: '', contents: ['test.json'] },
{ method: 'stat', path: 'test.json', isDir: false },
{
method: 'readFile',
path: 'test.json',
contents: '{"hi":"ho"}',
},
{ method: 'mkdir', path: 'dist' },
{
method: 'writeFile',
path: 'dist/test.json',
contents: '{"hi":"ho"}',
},
{ method: 'stat', path: '', isDir: true },
{
method: 'watch',
path: '',
options: { recursive: true, signal: undefined },
watcher,
},
],
__dirname,
);
const wtchr = wtch({
entry: __dirname,
processors: [
{
include: (name) => name === 'test.json',
outDir: join(__dirname, 'dist'),
},
],
fs,
});
let i = 0;
for await (const manifest of wtchr) {
switch (i++) {
case 0:
assert.deepEqual(manifest, {
'test.json': 'test.json',
});
watchEvents[0].resolve({
eventType: 'change',
filename: 'test2.json',
});
break;
default:
throw new Error('NEIN');
}
}
fs.done();
});
await t.test('continues watching despite error', async () => {
const watchEvents: Deferred<WatchEvent>[] = [defer(), defer(), defer()];
const watcher = createWatcher(watchEvents.map(({ promise }) => promise));
const myErr1 = new Error('Err1');
const myErr2 = new Error('Err2');
const fs = createFs(
[
{ method: 'stat', path: 'test.json', isDir: false },
{
method: 'readFile',
path: 'test.json',
contents: myErr1,
},
{ method: 'stat', path: 'test.json', isDir: false },
{
method: 'watch',
path: 'test.json',
options: { recursive: false, signal: undefined },
watcher,
},
{ method: 'stat', path: 'test.json', isDir: false },
{
method: 'readFile',
path: 'test.json',
contents: '{"hi":"ho"}',
},
{ method: 'mkdir', path: 'dist' },
{
method: 'writeFile',
path: 'dist/test.json',
contents: '{"hi":"ho"}',
},
{
method: 'readFile',
path: 'test.json',
contents: myErr2,
},
{
method: 'readFile',
path: 'test.json',
contents: '{"le":"la"}',
},
{ method: 'mkdir', path: 'dist' },
{
method: 'writeFile',
path: 'dist/test.json',
contents: '{"le":"la"}',
},
],
__dirname,
);
let i = 0;
const wtchr = wtch({
entry: join(__dirname, 'test.json'),
onError: (err) => {
switch (i++) {
case 0:
assert.equal(err, myErr1);
watchEvents[0].resolve({
eventType: 'change',
filename: 'test.json',
});
break;
case 1:
assert.equal(err, myErr2);
watchEvents[2].resolve({
eventType: 'change',
filename: 'test.json',
});
break;
default:
throw err;
}
},
processors: [
{
outDir: join(__dirname, 'dist'),
},
],
fs,
});
let j = 0;
for await (const manifest of wtchr) {
switch (j++) {
case 0:
assert.deepEqual(manifest, {
'test.json': 'test.json',
});
watchEvents[1].resolve({
eventType: 'change',
filename: 'test.json',
});
break;
case 1:
assert.deepEqual(manifest, {
'test.json': 'test.json',
});
break;
default:
throw new Error('NEIN');
}
}
fs.done();
});
});
interface ExpectedReaddir {
method: 'readdir';
path: string;
contents: string[] | Error;
}
interface ExpectedStat {
method: 'stat';
path: string;
isDir: boolean | Error;
}
interface ExpectedMkdir {
method: 'mkdir';
path: string;
error?: Error;
}
interface ExpectedReadFile {
method: 'readFile';
path: string;
contents: Buffer | string | Error;
}
interface ExpectedWriteFile {
method: 'writeFile';
path: string;
contents: Buffer | string | Error;
}
interface ExpectedWatch {
method: 'watch';
path: string;
options: { recursive?: boolean; signal?: AbortSignal };
watcher: AsyncIterable<WatchEvent> | Error;
}
type ExpectedCall =
| ExpectedReaddir
| ExpectedStat
| ExpectedMkdir
| ExpectedReadFile
| ExpectedWriteFile
| ExpectedWatch;
type Deferred<T> = {
promise: Promise<T>;
resolve: (val: T) => void;
reject: (error: unknown) => void;
};
function defer<T>(): Deferred<T> {
let resolve: (val: T) => void;
let reject: (error: unknown) => void;
let promise = new Promise<T>((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
}
function createWatcher(events: Promise<WatchEvent | Error>[]) {
return {
async *[Symbol.asyncIterator]() {
for (const event of events) {
const next = await event;
if (next instanceof Error) {
throw next;
} else {
yield next;
}
}
},
};
}
function createFs(
expectedCalls: ExpectedCall[],
baseDir: string,
): WatchFs & { done: () => void } {
let i = 0;
const getNextCall = <M extends ExpectedCall>(
method: M['method'],
args: any,
): M => {
// console.log(`${i++} --- fs.${method}(${JSON.stringify(args)})`);
const next = expectedCalls.shift();
assert.ok(next !== undefined, `Unexpected call to fs.${method}`);
const { method: exMeth, ...exArgs } = next;
assert.ok(
exMeth === method,
`Expected fs.${exMeth}(${JSON.stringify(
exArgs,
)}) call - got fs.${method}(${JSON.stringify(args)})`,
);
return next as any;
};
return {
readdir: async (dir) => {
const { path, contents } = getNextCall<ExpectedReaddir>(
'readdir',
relative(baseDir, dir),
);
assert.equal(relative(baseDir, dir), path);
if (contents instanceof Error) {
throw contents;
}
return contents;
},
stat: async (p) => {
const { path, isDir } = getNextCall<ExpectedStat>(
'stat',
relative(baseDir, p),
);
assert.equal(relative(baseDir, p), path);
if (isDir instanceof Error) {
throw isDir;
}
return {
isDirectory: () => isDir,
};
},
mkdir: async (p) => {
const { path, error } = getNextCall<ExpectedMkdir>(
'mkdir',
relative(baseDir, p),
);
assert.equal(relative(baseDir, p), path);
if (error) {
throw error;
}
return undefined;
},
readFile: async (p) => {
const { path, contents } = getNextCall<ExpectedReadFile>(
'readFile',
relative(baseDir, p),
);
assert.equal(relative(baseDir, p), path);
if (contents instanceof Error) {
throw contents;
}
return contents instanceof Buffer ? contents : Buffer.from(contents);
},
writeFile: async (p, c) => {
const { path, contents } = getNextCall<ExpectedWriteFile>('writeFile', [
relative(baseDir, p),
c,
]);
assert.equal(relative(baseDir, p), path);
if (contents instanceof Error) {
throw contents;
}
assert.equal(c.toString(), contents.toString());
},
watch: (p, o) => {
const { path, options, watcher } = getNextCall<ExpectedWatch>('watch', [
relative(baseDir, p),
o,
]);
assert.equal(relative(baseDir, p), path);
assert.deepEqual(options, o);
if (watcher instanceof Error) {
throw watcher;
}
return watcher;
},
done() {
assert.ok(
expectedCalls.length === 0,
`Got ${expectedCalls.length} trailing expectations on fs`,
);
},
};
}