react-native-builder-bob
Version:
CLI to build JavaScript files for React Native libraries
166 lines • 6.07 kB
JavaScript
import os from 'node:os';
import path from 'node:path';
import fs from 'fs-extra';
import { beforeEach, expect, test, vi } from 'vitest';
import buildCommonJS from "../targets/commonjs.js";
import buildModule from "../targets/module.js";
import { spawn } from "../utils/spawn.js";
const report = {
info: vi.fn(),
warn: vi.fn(),
success: vi.fn(),
error: vi.fn(),
};
beforeEach(() => {
vi.mocked(report.warn).mockClear();
});
const buildLibrary = async (root, { files, packageJson, targets, output = path.join(root, 'lib'), }) => {
await fs.writeJSON(path.join(root, 'package.json'), {
name: 'library',
version: '1.0.0',
...packageJson,
});
await Promise.all(Object.entries(files).map(async ([name, content]) => fs.outputFile(path.join(root, name), content)));
const variants = {
commonjs: targets.includes('commonjs'),
module: targets.includes('module'),
};
for (const target of targets) {
const build = target === 'commonjs' ? buildCommonJS : buildModule;
await build({
root,
source: path.join(root, 'src'),
output: path.join(output, target),
exclude: '**/{__tests__,__fixtures__,__mocks__}/**',
options: {
esm: true,
// The default preset loads the babel plugin from the built files
// So it can't be used when running tests against the source code
configFile: path.resolve(import.meta.dirname, '../__fixtures__/babel.config.js'),
},
variants,
report,
});
}
};
const getImportsWarnings = () => vi
.mocked(report.warn)
.mock.calls.map(([message]) => message)
.filter((message) => message.includes("won't apply to the files in"));
const readPackageJson = async (output) => {
const packageJsonPath = path.join(output, 'package.json');
return (await fs.pathExists(packageJsonPath))
? fs.readJSON(packageJsonPath)
: undefined;
};
test.each([
['module', undefined, { type: 'module' }],
['module', 'commonjs', { type: 'module' }],
['module', 'module', undefined],
['commonjs', undefined, { type: 'commonjs' }],
['commonjs', 'module', { type: 'commonjs' }],
['commonjs', 'commonjs', undefined],
])('writes package.json for %s output when the root type is %s', async (target, type, expected) => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'bob-compile-'));
try {
await buildLibrary(root, {
packageJson: { type },
files: { 'src/index.ts': 'export const foo = 42;\n' },
targets: [target],
});
expect(await readPackageJson(path.join(root, 'lib', target))).toEqual(expected);
}
finally {
await fs.remove(root);
}
});
test.each(['module', 'commonjs'])('resolves package imports in %s output when package.json has the same type', async (target) => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'bob-compile-'));
try {
await buildLibrary(root, {
packageJson: {
type: target,
imports: {
'#internal/*': `./lib/${target}/internal/*.js`,
},
},
files: {
'src/index.ts': [
"import { foo } from '#internal/foo';",
'',
'console.log(foo);',
].join('\n'),
'src/internal/foo.ts': "export const foo = 'bar';\n",
},
targets: [target],
});
expect(getImportsWarnings()).toEqual([]);
const stdout = await spawn(process.execPath, [path.join(root, 'lib', target, 'index.js')], { cwd: root });
expect(stdout).toBe('bar');
}
finally {
await fs.remove(root);
}
});
test.each([
[['module', 'commonjs'], 'module'],
[['commonjs'], 'commonjs'],
])('warns about package imports for %j targets only in %s output', async (targets, warned) => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'bob-compile-'));
try {
await buildLibrary(root, {
packageJson: { imports: { '#internal/*': './src/internal/*.ts' } },
files: { 'src/index.ts': 'export const foo = 42;\n' },
targets: [...targets],
});
expect(getImportsWarnings()).toEqual([
expect.stringContaining(path.join('lib', warned)),
]);
}
finally {
await fs.remove(root);
}
});
test('does not warn about an empty imports field', async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'bob-compile-'));
try {
await buildLibrary(root, {
packageJson: { imports: {} },
files: { 'src/index.ts': 'export const foo = 42;\n' },
targets: ['module'],
});
expect(await readPackageJson(path.join(root, 'lib/module'))).toEqual({
type: 'module',
});
expect(getImportsWarnings()).toEqual([]);
}
finally {
await fs.remove(root);
}
});
test.each(['module', undefined])('writes package.json without warning when the output is outside the project and the root type is %s', async (type) => {
const parent = await fs.mkdtemp(path.join(os.tmpdir(), 'bob-compile-'));
const root = path.join(parent, 'library');
try {
await fs.mkdirp(root);
await buildLibrary(root, {
packageJson: {
type,
imports: {
'#internal/*': './src/internal/*.ts',
},
},
files: { 'src/index.ts': 'export const foo = 42;\n' },
targets: ['module'],
output: path.join(parent, 'dist'),
});
expect(await readPackageJson(path.join(parent, 'dist/module'))).toEqual({
type: 'module',
});
expect(getImportsWarnings()).toEqual([]);
}
finally {
await fs.remove(parent);
}
});
//# sourceMappingURL=compile.test.js.map