Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wip: v1 rc #44

Merged
merged 35 commits into from
Dec 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
d2669e3
wip: implicit src
cometkim Jun 16, 2022
efb2885
add more tests
cometkim Jun 30, 2022
0a659f9
spec change & more tests
cometkim Jul 6, 2022
a78fd20
all tests for JS are now passed
cometkim Nov 17, 2022
f1e5944
refactor config
cometkim Nov 20, 2022
885d069
minify by filename convention
cometkim Nov 20, 2022
bc1a409
wip rewrite
cometkim Nov 21, 2022
3c23d36
rename parsedConfig -> context
cometkim Nov 21, 2022
afb869b
fix typo
cometkim Nov 21, 2022
398a5d2
more test
cometkim Nov 21, 2022
ba3be41
wip: build command and expand context module more
cometkim Nov 22, 2022
ba4b38f
wip
cometkim Nov 26, 2022
b7677dd
build bundle and files
cometkim Nov 26, 2022
cc068a9
add tsconfig tests
cometkim Nov 27, 2022
3289a24
set declaration=true as default
cometkim Nov 27, 2022
536476a
wip: types entries
cometkim Nov 27, 2022
202bc67
added failing tests for typescript
cometkim Nov 28, 2022
b5ee1af
types entry for conditional exports
cometkim Dec 3, 2022
c2243f5
fix broken tests
cometkim Dec 3, 2022
abd08ad
validate types entry extension
cometkim Dec 3, 2022
4ebb4ba
validate types entry format
cometkim Dec 3, 2022
97c186e
validate types entry order
cometkim Dec 3, 2022
e59fc49
entry tests all passed
cometkim Dec 3, 2022
ea5f462
warn if module for dts isn't determined
cometkim Dec 3, 2022
cba71e5
update browserslist
cometkim Dec 3, 2022
a125c7c
entries on different rootDir, outDir
cometkim Dec 3, 2022
67a8139
fix context parsing
cometkim Dec 3, 2022
9f90d8a
allow rootDir=outDir in TypeScript project
cometkim Dec 3, 2022
de72c18
refactor
cometkim Dec 3, 2022
be21c0c
Node v16 requirement
cometkim Dec 3, 2022
6f3efea
build script
cometkim Dec 3, 2022
621496b
format messages
cometkim Dec 3, 2022
8c9ae0b
support bin entry
cometkim Dec 3, 2022
5f63591
state: self-hosting binary
cometkim Dec 4, 2022
1b3ccb8
prepare major release
cometkim Dec 4, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .changeset/tender-boxes-approve.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"nanobundle": major
---

v1 features

- support multiple entries
- support nested conditional exports
- source inference from rootDir and outDir
- enable tree-shaking by default
- pretty reporter
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
.yarn/** linguist-vendored
.yarn/releases/* binary linguist-vendored

*.typegen.ts linguist-generated
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
node_modules/
coverage/

/bin.mjs
/bin.min.mjs

.yarn/*
!.yarn/patches
Expand Down
768 changes: 0 additions & 768 deletions .yarn/releases/yarn-3.1.1.cjs

This file was deleted.

807 changes: 807 additions & 0 deletions .yarn/releases/yarn-3.3.0.cjs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion .yarnrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ nmMode: hardlinks-global

nodeLinker: node-modules

yarnPath: .yarn/releases/yarn-3.1.1.cjs
yarnPath: .yarn/releases/yarn-3.3.0.cjs
16 changes: 16 additions & 0 deletions build.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import esbuild from 'esbuild';
import manifest from './package.json' assert { type: 'json' };

esbuild.build({
entryPoints: ['src/bin.ts'],
outfile: 'bin.min.mjs',
bundle: true,
write: true,
treeShaking: true,
sourcemap: false,
minify: true,
format: 'esm',
platform: 'node',
target: ['node16'],
external: Object.keys(manifest.dependencies),
});
28 changes: 19 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "0.0.28",
"type": "module",
"license": "MIT",
"bin": "./bin.mjs",
"bin": "./bin.min.mjs",
"repository": {
"type": "git",
"url": "https://github.com/cometkim/nanobundle.git"
Expand All @@ -17,7 +17,8 @@
},
"scripts": {
"prepack": "yarn build",
"build": "esbuild src/bin.ts --bundle --format=esm --platform=node --target=node14.8 --outfile=bin.mjs --external:meow --external:esbuild --external:browserslist --external:typescript --external:pretty-bytes --external:tsconfck",
"build": "node build.mjs",
"build:self": "node bin.min.mjs build",
"dev": "vitest --watch",
"test": "vitest",
"changeset": "changeset"
Expand All @@ -26,7 +27,7 @@
"bin.mjs"
],
"engines": {
"node": ">=14.8.0"
"node": ">=16.0.0"
},
"peerDependencies": {
"typescript": "^3.7.0 || ^4.0.0"
Expand All @@ -37,18 +38,27 @@
}
},
"dependencies": {
"browserslist": "^4.19.3",
"@cometjs/core": "^2.1.0",
"browserslist": "^4.21.4",
"esbuild": "^0.14.23",
"kleur": "^4.1.5",
"meow": "^10.1.2",
"pretty-bytes": "^6.0.0",
"tsconfck": "^1.2.0"
"semver": "^7.3.8",
"string-dedent": "^3.0.1",
"tsconfck": "^1.2.0",
"xstate": "^4.34.0"
},
"devDependencies": {
"@changesets/cli": "^2.20.0",
"@types/node": "^17.0.18",
"@types/semver": "^7.3.13",
"@xstate/cli": "^0.3.3",
"c8": "^7.11.0",
"typescript": "^4.5.5",
"vitest": "^0.5.7"
"pkg-types": "^1.0.1",
"typescript": "^4.9.3",
"vite": "^3.2.4",
"vitest": "^0.25.2"
},
"packageManager": "yarn@3.1.1"
}
"packageManager": "yarn@3.3.0"
}
3 changes: 3 additions & 0 deletions src/__snapshots__/config.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Vitest Snapshot v1

exports[`parseConfig > flags > rootDir=outDir 1`] = `"Directory rootDir(.) and outDir(.) are conflict! Please specify different directory for one of them."`;
7 changes: 7 additions & 0 deletions src/__snapshots__/context.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Vitest Snapshot v1

exports[`parseConfig > flags > rootDir=outDir is not allowed without TypeScript 1`] = `
"\\"rootDir\\" (.) and \\"outDir\\" (.) are conflict!
Please specify different directory for one of them."
`;
20 changes: 20 additions & 0 deletions src/__snapshots__/entry.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Vitest Snapshot v1

exports[`getEntriesFromContext > bin entry only accepts js 1`] = `"Only JavaScript files are allowed for bin entry."`;

exports[`getEntriesFromContext > throw if "main" and "module" is on conflict 1`] = `
"Hint: Did you forgot to set \\"type\\" to 'module' for ESM-first approach?
"
`;

exports[`getEntriesFromContext - in TypeScript project > types entry does not accept nesting 1`] = `
"\\"types\\" entry must be .d.ts file and cannot be nested!
"
`;

exports[`getEntriesFromContext - in TypeScript project > types entry must has .d.ts extension 1`] = `"Only .d.ts or .d.cts or .d.mts allowed for \\"types\\" entry."`;

exports[`getEntriesFromContext - in TypeScript project > types entry must occur first in conditional exports 1`] = `
"\\"types\\" entry must occur first in conditional exports for correct type resolution.
"
`;
196 changes: 54 additions & 142 deletions src/bin.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,27 @@
#!/usr/bin/env node

import { performance } from 'node:perf_hooks';
import * as fs from 'node:fs';
import * as path from 'node:path';
import { parseNative } from 'tsconfck';
import type {
Program as TSProgram,
CompilerOptions as TSCompilerOptions,
} from 'typescript';
import { parse as parseTsConfig } from 'tsconfck';
import dedent from 'string-dedent';

import type { Reporter } from './report';
import { cli } from './cli';
import { loadConfig } from './config';
import { ConsoleReporter } from './reporter';
import { loadTargets } from './target';
import { loadImportMaps, normalizeImportMaps, validateImportMaps } from './importMaps';
import { getEntriesFromConfig } from './entry';
import { buildCommand } from './commands/build';
import { makePlugin as makeEmbedPlugin } from './plugins/esbuildEmbedPlugin';
import { makePlugin as makeImportMapsPlugin } from './plugins/esbuildImportMapsPlugin';
import { loadManifest } from './manifest';
import { parseConfig } from './context';
import { getEntriesFromContext } from './entry';
import * as formatUtils from './formatUtils';
import { NanobundleError } from './errors';

import { buildCommand } from './commands/build/build';

const { flags, input } = cli;
const [command] = input;
const noop = () => {};
const debugEnabled = process.env.DEBUG === 'true';

const {
cwd: basePath,
external: forceExternalDependencies,
minify,
sourcemap,
standalone,
} = flags;

const reporter: Reporter = {
debug: debugEnabled ? console.debug : noop,
info: console.info,
warn: console.warn,
error: console.error,
};
const reporter = new ConsoleReporter(console);
reporter.level = process.env.DEBUG === 'true' ? 'debug' : 'default';

const resolvePath = (file: string) => path.resolve(basePath, file);
const resolve = (cwd: string, subpath: string) => path.resolve(cwd, subpath);

try {
switch (command) {
Expand All @@ -48,137 +30,67 @@ try {
}

case 'build': {
const startedAt = performance.now();

const config = await loadConfig({ resolvePath });
const sourceFile = config.source && resolvePath(config.source);
if (!sourceFile || !fs.existsSync(sourceFile)) {
throw new Error('`"source"` field must be specified in the package.json');
}

reporter.debug(`build ${config.name || 'unnamed'} package`);
reporter.debug(`load source from ${sourceFile}`);
const manifest = await loadManifest({
cwd: flags.cwd,
resolve,
});
reporter.info(`build ${manifest.name || 'unnamed'} package`);

const externalDependencies = [
...(config.dependencies ? Object.keys(config.dependencies) : []),
...(config.peerDependencies ? Object.keys(config.peerDependencies) : []),
...forceExternalDependencies,
];
const importMaps = await loadImportMaps(
flags.importMaps,
{ resolvePath },
);
const webImportMaps = validateImportMaps(
normalizeImportMaps(importMaps, 'web'),
{ resolvePath },
const tsconfigResult = await parseTsConfig(flags.tsconfig, {
resolveWithEmptyIfConfigNotFound: true,
});
const tsconfigPath = (
tsconfigResult.tsconfigFile !== 'no_tsconfig_file_found'
? tsconfigResult.tsconfigFile
: undefined
);
const nodeImportMaps = validateImportMaps(
normalizeImportMaps(importMaps, 'node'),
{ resolvePath },
const tsconfig = (
tsconfigResult.tsconfigFile !== 'no_tsconfig_file_found'
? tsconfigResult.tsconfig
: undefined
);
const embedPlugin = makeEmbedPlugin({
if (tsconfigPath) {
reporter.debug(`load tsconfig from ${tsconfigPath}`);
}
const targets = await loadTargets({ basePath: flags.cwd });
const context = parseConfig({
flags,
targets,
manifest,
tsconfig,
tsconfigPath,
resolve,
reporter,
standalone,
externalDependencies,
forceExternalDependencies,
});
const webImportMapsPlugin = makeImportMapsPlugin({
name: 'web',
imports: webImportMaps.imports,
resolvePath,
});
const nodeImportMapsPlugin = makeImportMapsPlugin({
name: 'node',
imports: nodeImportMaps.imports,
resolvePath,
});
const webPlugins = [
webImportMapsPlugin,
embedPlugin,
];
const nodePlugins = [
nodeImportMapsPlugin,
embedPlugin,
];

let tsconfig: string | undefined;
let tsProgram: TSProgram | undefined;
if (flags.dts && config.types) {
const ts = await import('typescript').then(mod => mod.default);
const tsconfigResult = await parseNative(flags.tsconfig);

tsconfig = tsconfigResult.tsconfigFile;
reporter.debug(`load tsconfig from ${tsconfig}`);

const compilerOptions: TSCompilerOptions = {
...tsconfigResult.result.options,
reporter.debug(`load targets ${context.targets.join(', ')}`);

allowJs: true,
incremental: false,
skipLibCheck: true,
declaration: true,
emitDeclarationOnly: true,
};

if (compilerOptions.noEmit) {
reporter.warn('Ignored `compilerOptions.noEmit` since the package required `types` entry.');
reporter.warn('You can still disable emitting declaration via `--dts=false` option');
compilerOptions.noEmit = false;
}

const host = ts.createCompilerHost(compilerOptions);
tsProgram = ts.createProgram([sourceFile], compilerOptions, host);
}

const targets = await loadTargets({ basePath });
reporter.debug(`targets to ${targets.join(', ')}`);

const entries = getEntriesFromConfig({
config,
const entries = getEntriesFromContext({
context,
reporter,
resolvePath,
resolve,
});

await buildCommand({
reporter,
sourceFile,
context,
entries,
targets,
tsconfig,
resolvePath,
minify,
sourcemap,
webPlugins,
nodePlugins,
});

if (tsProgram) {
reporter.info('Emitting .d.ts files...');

const { emittedFiles } = tsProgram.emit();
reporter.debug('emitted', emittedFiles);
}

const endedAt = performance.now();
const elapsedTime = (endedAt - startedAt).toFixed(1);
reporter.info(`\n⚡ Done in ${elapsedTime}ms.`);

break;
}

case 'watch': {
throw new Error('sorry, not implemeted yet');
}

default: {
throw new Error(`
Command "${command}" is not available.
throw new NanobundleError(dedent`
Command "${command}" is not available.
Run \`nanobundle --help\` for more detail.`,
);
Run ${formatUtils.command('nanobundle --help')} for usage.
`);
}
}
} catch (error) {
reporter.error(error);
if (error instanceof NanobundleError) {
reporter.error(error.message);
} else {
reporter.captureException(error);
}
process.exit(1);
}
Loading