Skip to content

Commit

Permalink
new: Rework automatic entry points and exports. (#27)
Browse files Browse the repository at this point in the history
* Move exports code to Package.

* Add types.

* Improve entry points.

* Add exports.
  • Loading branch information
milesj authored Feb 8, 2021
1 parent 18db408 commit 3391d3c
Show file tree
Hide file tree
Showing 10 changed files with 726 additions and 384 deletions.
17 changes: 16 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"test": "beemo jest",
"type": "beemo typescript --noEmit",
"prerelease": "yarn run clean && yarn run setup && yarn run pack && yarn run ci",
"pack": "NODE_ENV=production yarn run packemon pack --addEngines --declaration=api",
"pack": "NODE_ENV=production yarn run packemon pack --addEngines --addExports --declaration=api",
"packemon": "node ./packemon.js"
},
"repository": "https://github.com/milesj/packemon.git",
Expand All @@ -32,6 +32,21 @@
"main": "./lib/index.js",
"types": "./dts/index.d.ts",
"bin": "./lib/bin.js",
"exports": {
"./package.json": "./package.json",
"./babel": {
"node": "./lib/babel.js",
"types": "./dts/babel.d.ts"
},
"./bin": {
"node": "./lib/bin.js",
"types": "./dts/bin.d.ts"
},
".": {
"node": "./lib/index.js",
"types": "./dts/index.d.ts"
}
},
"sideEffects": false,
"engines": {
"node": ">=10.3.0",
Expand Down
4 changes: 0 additions & 4 deletions src/Artifact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,6 @@ export default abstract class Artifact<T extends object = {}> {
return this.state === 'building';
}

postBuild(options: BuildOptions): Awaitable {}

preBuild(options: BuildOptions): Awaitable {}

startup() {}

toString(): string {
Expand Down
118 changes: 44 additions & 74 deletions src/BundleArtifact.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { rollup, RollupCache } from 'rollup';
import { isObject, Path, SettingMap, toArray } from '@boost/common';
import { Path, toArray } from '@boost/common';
import { createDebugger, Debugger } from '@boost/debug';
import Artifact from './Artifact';
import { DEFAULT_FORMAT, NODE_SUPPORTED_VERSIONS, NPM_SUPPORTED_VERSIONS } from './constants';
import { getRollupConfig } from './rollup/config';
import { BuildOptions, BundleBuild, Format, Platform, Support } from './types';
import { BuildOptions, BundleBuild, Format, PackageExportPaths, Platform, Support } from './types';

export default class BundleArtifact extends Artifact<BundleBuild> {
cache?: RollupCache;
Expand Down Expand Up @@ -83,18 +83,21 @@ export default class BundleArtifact extends Artifact<BundleBuild> {
};
}),
);
}

postBuild({ addEngines, addExports }: BuildOptions): void {
this.addEntryPointsToPackageJson();

if (addEngines) {
if (options.addEngines) {
this.addEnginesToPackageJson();
}
}

if (addExports) {
this.addExportsToPackageJson();
findEntryPoint(formats: Format[]): string {
// eslint-disable-next-line no-restricted-syntax
for (const format of formats) {
if (this.builds.some((build) => build.format === format)) {
return this.getOutputMetadata(format).path;
}
}

return '';
}

getLabel(): string {
Expand Down Expand Up @@ -133,6 +136,38 @@ export default class BundleArtifact extends Artifact<BundleBuild> {
};
}

getPackageExports(): PackageExportPaths {
const paths: PackageExportPaths = {};
let libPath = '';

this.builds.forEach(({ format }) => {
const { path } = this.getOutputMetadata(format);

if (format === 'mjs' || format === 'esm') {
paths.import = path;
} else if (format === 'cjs') {
paths.require = path;
} else if (format === 'lib') {
libPath = path;
}

// Webpack and Rollup support
if (format === 'esm') {
paths.module = path;
}
});

// Must come after import/require
if (libPath) {
paths.default = libPath;
}

return {
[this.platform === 'native' ? 'react-native' : this.platform]:
Object.keys(paths).length === 1 && libPath ? paths.default : paths,
};
}

getStatsFileName(): string {
return `stats-${this.getStatsTitle().replace(/\//gu, '-')}.html`;
}
Expand Down Expand Up @@ -163,69 +198,4 @@ export default class BundleArtifact extends Artifact<BundleBuild> {
});
}
}

protected addEntryPointsToPackageJson() {
this.debug('Adding entry points to `package.json`');

const pkg = this.package.packageJson;

this.builds.forEach(({ format }) => {
const { path } = this.getOutputMetadata(format);
const isNode = format === 'lib' || format === 'cjs' || format === 'mjs';

if (this.outputName === 'index') {
if (isNode) {
pkg.main = path;
} else if (format === 'esm') {
pkg.module = path;
} else if (format === 'umd') {
pkg.browser = path;
}
}

// Bin field may be an object
if (this.outputName === 'bin' && !isObject(pkg.bin) && isNode) {
pkg.bin = path;
}
});
}

protected addExportsToPackageJson() {
const pkg = this.package.packageJson;
const paths: SettingMap = {};
let libPath = '';

this.builds.forEach(({ format }) => {
const { path } = this.getOutputMetadata(format);

if (format === 'mjs' || format === 'esm') {
paths.import = path;
} else if (format === 'cjs') {
paths.require = path;
} else if (format === 'lib') {
libPath = path;
}
});

// Must come after import/require
if (libPath) {
paths.default = libPath;
}

const exportCount = Object.keys(paths).length;

if (exportCount > 0) {
this.debug('Adding `exports` to `package.json`');

if (!pkg.exports) {
pkg.exports = {};
}

Object.assign(pkg.exports, {
'./package.json': './package.json',
[this.outputName === 'index' ? '.' : `./${this.outputName}`]:
exportCount === 1 && libPath ? paths.default : paths,
});
}
}
}
93 changes: 90 additions & 3 deletions src/Package.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
/* eslint-disable require-atomic-updates, no-param-reassign, @typescript-eslint/member-ordering */

import fs from 'fs-extra';
import { Memoize, optimal, Path, toArray } from '@boost/common';
import { isObject, Memoize, optimal, PackageStructure, Path, toArray } from '@boost/common';
import { createDebugger, Debugger } from '@boost/debug';
import Artifact from './Artifact';
import BundleArtifact from './BundleArtifact';
import { FORMATS_BROWSER, FORMATS_NATIVE, FORMATS_NODE } from './constants';
import { loadModule } from './helpers/loadModule';
import Project from './Project';
Expand All @@ -12,10 +13,13 @@ import {
BuildOptions,
FeatureFlags,
PackageConfig,
PackageExportPaths,
PackageExports,
PackemonPackage,
PackemonPackageConfig,
TSConfigStructure,
} from './types';
import TypesArtifact from './TypesArtifact';

export default class Package {
readonly artifacts: Artifact[] = [];
Expand Down Expand Up @@ -61,9 +65,7 @@ export default class Package {
try {
artifact.state = 'building';

await artifact.preBuild(options);
await artifact.build(options);
await artifact.postBuild(options);

artifact.state = 'passed';
} catch (error) {
Expand All @@ -76,6 +78,14 @@ export default class Package {
}),
);

// Add package entry points based on artifacts
this.addEntryPoints();

// Add package `exports` based on artifacts
if (options.addExports) {
this.addExports();
}

// Sync `package.json` in case it was modified
await this.syncPackageJson();
}
Expand Down Expand Up @@ -262,4 +272,81 @@ export default class Package {

return result;
}

protected addEntryPoints() {
this.debug('Adding entry points to `package.json`');

let mainEntry = '';
let moduleEntry = '';

this.artifacts.forEach((artifact) => {
if (artifact instanceof BundleArtifact) {
if (artifact.outputName === 'index') {
if (!mainEntry || artifact.platform === 'node') {
mainEntry = artifact.findEntryPoint(['lib', 'cjs', 'mjs']);
}

if (!moduleEntry) {
moduleEntry = artifact.findEntryPoint(['esm', 'mjs']);
}
}

if (
artifact.outputName === 'bin' &&
artifact.platform === 'node' &&
!isObject(this.packageJson.bin)
) {
this.packageJson.bin = artifact.findEntryPoint(['lib', 'cjs', 'mjs']);
}
} else if (artifact instanceof TypesArtifact) {
this.packageJson.types = './dts/index.d.ts';
}
});

if (mainEntry) {
this.packageJson.main = mainEntry;

if (mainEntry.endsWith('mjs')) {
this.packageJson.type = 'module';
} else if (mainEntry.endsWith('cjs')) {
this.packageJson.type = 'commonjs';
}
}

if (moduleEntry) {
this.packageJson.module = moduleEntry;
}
}

protected addExports() {
this.debug('Adding `exports` to `package.json`');

const exports: PackageExports = {
'./package.json': './package.json',
};

const mapConditionsToPath = (basePath: string, conditions: PackageExportPaths | string) => {
const path = basePath.replace('/index', '');

if (!exports[path]) {
exports[path] = {};
}

Object.assign(exports[path], conditions);
};

this.artifacts.forEach((artifact) => {
if (artifact instanceof BundleArtifact) {
mapConditionsToPath(`./${artifact.outputName}`, artifact.getPackageExports());
}

if (artifact instanceof TypesArtifact) {
Object.entries(artifact.getPackageExports()).forEach(([path, conditions]) => {
mapConditionsToPath(path, conditions);
});
}
});

this.packageJson.exports = exports as PackageStructure['exports'];
}
}
24 changes: 19 additions & 5 deletions src/TypesArtifact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@ import { Path } from '@boost/common';
import { createDebugger, Debugger } from '@boost/debug';
import { Extractor, ExtractorConfig } from '@microsoft/api-extractor';
import Artifact from './Artifact';
import { APIExtractorStructure, DeclarationType, TSConfigStructure, TypesBuild } from './types';
import {
APIExtractorStructure,
DeclarationType,
PackageExports,
TSConfigStructure,
TypesBuild,
} from './types';

// eslint-disable-next-line
const extractorConfig = require(path.join(__dirname, '../api-extractor.json')) as {
Expand Down Expand Up @@ -70,10 +76,6 @@ export default class TypesArtifact extends Artifact<TypesBuild> {
}
}

postBuild(): void {
this.package.packageJson.types = './dts/index.d.ts';
}

getLabel(): string {
return 'types';
}
Expand All @@ -82,6 +84,18 @@ export default class TypesArtifact extends Artifact<TypesBuild> {
return ['dts'];
}

getPackageExports(): PackageExports {
const exports: PackageExports = {};

this.builds.forEach(({ outputName }) => {
exports[`./${outputName}`] = {
types: `./dts/${outputName}.d.ts`,
};
});

return exports;
}

toString() {
return `types (dts)`;
}
Expand Down
19 changes: 19 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,25 @@ export interface PackageConfig {
support: Support;
}

// PACKAGE EXPORTS
// https://webpack.js.org/guides/package-exports

export type PackageExportConditions =
| 'browser'
| 'default'
| 'import'
| 'module'
| 'node'
| 'react-native'
| 'require'
| 'types';

export type PackageExportPaths = {
[K in PackageExportConditions]?: PackageExportPaths | string;
};

export type PackageExports = Record<string, PackageExportPaths | string>;

// BUILD

export type ArtifactState = 'building' | 'failed' | 'passed' | 'pending';
Expand Down
Loading

0 comments on commit 3391d3c

Please sign in to comment.