Skip to content

Commit

Permalink
wip rewrite
Browse files Browse the repository at this point in the history
  • Loading branch information
cometkim committed Nov 21, 2022
1 parent 885d069 commit bc1a409
Show file tree
Hide file tree
Showing 20 changed files with 533 additions and 82 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"@changesets/cli": "^2.20.0",
"@types/node": "^17.0.18",
"c8": "^7.11.0",
"pkg-types": "^1.0.1",
"typescript": "^4.5.5",
"vite": "^3.2.4",
"vitest": "^0.25.2"
Expand Down
65 changes: 38 additions & 27 deletions src/bin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,36 @@
import { performance } from 'node:perf_hooks';
import * as fs from 'node:fs';
import * as path from 'node:path';
import { parseNative } from 'tsconfck';
import { parse as parseTsConfig } from 'tsconfck';
import type {
Program as TSProgram,
CompilerOptions as TSCompilerOptions,
} from 'typescript';

import type { Reporter } from './report';
import { cli } from './cli';
import { loadConfig } from './config';
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 './config';

const { flags, input } = cli;
const [command] = input;
const noop = () => {};
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 resolvePath = (file: string) => path.resolve(basePath, file);
const resolvePath = (cwd: string, subpath: string) => path.resolve(cwd, subpath);

try {
switch (command) {
Expand All @@ -50,14 +43,33 @@ 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');
const manifest = await loadManifest({
cwd: flags.cwd,
resolvePath,
});
reporter.debug(`build ${manifest.name || 'unnamed'} package`);

const tsconfigResult = await parseTsConfig(flags.tsconfig, { resolveWithEmptyIfConfigNotFound: true });
const tsconfigPath = (
tsconfigResult.tsconfigFile !== 'no_tsconfig_file_found'
? tsconfigResult.tsconfigFile
: undefined
);
const tsconfig = (
tsconfigResult.tsconfigFile !== 'no_tsconfig_file_found'
? tsconfigResult.tsconfig
: undefined
);
if (tsconfigPath) {
reporter.debug(`load tsconfig from ${tsconfigPath}`);
}

reporter.debug(`build ${config.name || 'unnamed'} package`);
reporter.debug(`load source from ${sourceFile}`);
const config = parseConfig({
flags,
manifest,
tsconfig,
tsconfigPath,
});

const externalDependencies = [
...(config.dependencies ? Object.keys(config.dependencies) : []),
Expand All @@ -68,8 +80,8 @@ try {
flags.importMaps,
{ resolvePath },
);
const webImportMaps = validateImportMaps(
normalizeImportMaps(importMaps, 'web'),
const defaultImportMaps = validateImportMaps(
normalizeImportMaps(importMaps, 'natural'),
{ resolvePath },
);
const nodeImportMaps = validateImportMaps(
Expand All @@ -82,26 +94,25 @@ try {
externalDependencies,
forceExternalDependencies,
});
const webImportMapsPlugin = makeImportMapsPlugin({
name: 'web',
imports: webImportMaps.imports,
const defaultImportMapsPlugin = makeImportMapsPlugin({
name: 'default',
imports: defaultImportMaps.imports,
resolvePath,
});
const nodeImportMapsPlugin = makeImportMapsPlugin({
name: 'node',
imports: nodeImportMaps.imports,
resolvePath,
});
const webPlugins = [
webImportMapsPlugin,
const defaultPlugins = [
defaultImportMapsPlugin,
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);
Expand All @@ -122,7 +133,7 @@ try {

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');
reporter.warn('You can still disable emitting declaration via `--no-dts` option');
compilerOptions.noEmit = false;
}

Expand Down
130 changes: 130 additions & 0 deletions src/build.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import * as path from 'node:path';
import type {
Format,
BuildOptions as EsbuildOptions,
} from 'esbuild';

import type { PathResolver } from './common';
import type { ParsedConfig } from './config';
import type { Entry } from './entry';
import type { EntryGroup } from './entryGroup';
import type { Reporter } from './report';

import * as fsUtils from './fsUtils';
import { optionsFromHash } from './entryGroup';

interface BuildOptions extends EsbuildOptions {
bundle: boolean,
write: boolean,
define: Record<string, string>,
tsconfig: string,
minify: boolean,
sourcemap: boolean,
format: Format,
target: string[],
outdir: string,
entryPoints: Record<string, string>,

nanobundlePlatform: Entry['platform'],
}

interface MakeBuildOptions {
(props: {
resolvePath: PathResolver;
reporter: Reporter,
config: ParsedConfig,
entryGroup: EntryGroup,
targets: string[],
}): Promise<BuildOptions[]>;
}
export const makeBuildOptions: MakeBuildOptions = async ({
resolvePath,
reporter,
config,
entryGroup,
targets,
}) => {
const buildOptions: BuildOptions[] = [];

for (const [optionsHash, groupEntries] of Object.entries(entryGroup)) {
const options = optionsFromHash(optionsHash);

const entries = await Promise.all(
groupEntries
.map(entry => ensureSourceFile({ entry, config, resolvePath })),
);
const entryPoints = Object.fromEntries(entries);

const target = [...targets];
if (options.platform === 'node' && !target.some(target => target.startsWith('node'))) {
target.push('node14');
}

const buildOption: BuildOptions = {
bundle: true,
write: false,
target,
entryPoints,
outdir: resolvePath(
config.cwd,
config.outDir
),
format: (
// It should not be file or json module.
options.module === 'esmodule'
? 'esm'
: 'cjs'
),
minify: options.minify,
sourcemap: options.sourcemap,
define: {},
...options.mode === 'development' && {
define: {
'process.env.NODE_ENV': JSON.stringify('development'),
},
},
...options.mode === 'production' && {
define: {
'process.env.NODE_ENV': JSON.stringify('production'),
},
},
tsconfig: (
path.isAbsolute(config.tsconfigPath)
? config.tsconfigPath
: resolvePath(config.cwd, config.tsconfigPath)
),

nanobundlePlatform: options.platform,
};
buildOptions.push(buildOption);
}

return buildOptions;
};

interface EnsureSourceFile {
(props: {
entry: Entry,
config: ParsedConfig,
resolvePath: PathResolver,
}): Promise<[string, string]>;
}
const ensureSourceFile: EnsureSourceFile = async ({
entry,
config,
resolvePath,
}) => {
let sourceFile;
for (const candidate of entry.sourceFile) {
const path = resolvePath(config.cwd, candidate);
if (await fsUtils.exists(path)) {
sourceFile = path;
}
}

if (!sourceFile) {
throw new Error(`Couldn't resolve source file for entry \`${entry.key}\``);
}

return [entry.outputFile, sourceFile];
}
Empty file added src/commands/build-dts.ts
Empty file.
13 changes: 4 additions & 9 deletions src/commands/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,16 @@ import prettyBytes from 'pretty-bytes';
import type { Entry } from '../entry';
import type { Reporter } from '../report';
import { formatModule } from '../utils';
import type { PathResolver } from '../common';
import type { ParsedConfig } from '../config';

const gzip = promisify(zlib.gzip);
const brotli = promisify(zlib.brotliCompress);

type BuildCommandOptions = {
config: ParsedConfig,
reporter: Reporter,
sourceFile: string,
entries: Entry[],
targets: string[],
minify: boolean,
sourcemap: boolean,
resolvePath: (path: string) => string,
tsconfig?: string,
webPlugins?: Plugin[],
nodePlugins?: Plugin[],
resolvePath: PathResolver,
};

export async function buildCommand({
Expand Down
1 change: 1 addition & 0 deletions src/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type PathResolver = (cwd: string, path: string) => string;
Loading

0 comments on commit bc1a409

Please sign in to comment.