Skip to content

Commit

Permalink
fix(packageparser): remove support for invalid exports shapes (#336)
Browse files Browse the repository at this point in the history
  • Loading branch information
Comandeer authored Nov 24, 2024
1 parent 3d4ab38 commit 64c828b
Show file tree
Hide file tree
Showing 19 changed files with 206 additions and 107 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

---

## [0.26.0]
### Changed
* [#335]: **BREAKING CHANGE**: invalid `exports` shapes (both subpaths and `import`) are no longer supported.

## [0.25.0] – 2024-11-16
### Added
* [#275]: support for `exports` as a string.
Expand Down Expand Up @@ -445,7 +449,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
[#321]: https://github.com/Comandeer/rollup-lib-bundler/issues/321
[#327]: https://github.com/Comandeer/rollup-lib-bundler/issues/327
[#332]: https://github.com/Comandeer/rollup-lib-bundler/issues/332
[#335]: https://github.com/Comandeer/rollup-lib-bundler/issues/335

[0.26.0]: https://github.com/Comandeer/rollup-lib-bundler/compare/v0.25.0...v0.26.0
[0.25.0]: https://github.com/Comandeer/rollup-lib-bundler/compare/v0.24.0...v0.25.0
[0.24.0]: https://github.com/Comandeer/rollup-lib-bundler/compare/v0.23.0...v0.24.0
[0.23.0]: https://github.com/Comandeer/rollup-lib-bundler/compare/v0.22.1...v0.23.0
Expand Down
27 changes: 27 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,12 @@
"devDependencies": {
"@comandeer/eslint-config": "^0.15.0",
"@comandeer/rollup-lib-bundler": "^0.23.0",
"@types/babel__preset-env": "^7.9.7",
"@types/mock-fs": "^4.13.4",
"@types/node": "^20.11.0",
"@types/semver": "^7.5.8",
"@types/sinon": "^17.0.3",
"@types/sourcemap-validator": "^2.1.2",
"ava": "^6.1.3",
"c8": "^10.1.2",
"commitplease": "^3.2.0",
Expand Down
2 changes: 1 addition & 1 deletion src/OutputController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ interface ConsoleLike {
error: ( ...args: Array<unknown> ) => void;
}

interface StackableError extends Error {
export interface StackableError extends Error {
stack?: string;
}

Expand Down
2 changes: 1 addition & 1 deletion src/bundler/bundleTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export default async function bundleTypes( {
absolute: true,
cwd: projectPath
} );
const emittedFiles = {};
const emittedFiles: Record<string, string> = {};

const host = ts.createCompilerHost( compilerOptions );
host.writeFile = ( filePath: string, contents: string ): void => {
Expand Down
7 changes: 7 additions & 0 deletions src/bundler/bundleTypesImports.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
declare module '@babel/plugin-syntax-import-assertions' {
export default Record<string, unknown>;
}

declare module 'rollup-plugin-preserve-shebang' {
export default function(): unknown;
}
7 changes: 3 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { rimraf } from 'rimraf';
import chalk from 'chalk';
import bundler from './bundler.js';
import OutputController from './OutputController.js';
import OutputController, { StackableError } from './OutputController.js';
import packageParser from './packageParser.js';
import getDistDirPaths from './utils/getDistDirPaths.js';
import { RollupLog } from 'rollup';

export default async function rlb(): Promise<void> {
const outputController = new OutputController();
Expand All @@ -21,15 +20,15 @@ export default async function rlb(): Promise<void> {
await rimraf( distPaths );

await bundler( {
onWarn( warning: RollupLog ): void {
onWarn( warning ): void {
outputController.addWarning( warning );
},
packageMetadata
} );

outputController.addLog( chalk.green.bold( 'Bundling complete!' ) );
} catch ( error ) {
await outputController.displayError( error );
await outputController.displayError( error as StackableError );
outputController.addLog( chalk.red.bold( 'Bundling failed!' ) );
} finally {
await outputController.display();
Expand Down
8 changes: 3 additions & 5 deletions src/packageParser.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { normalize as normalizePath } from 'pathe';
import semver from 'semver';
import loadAndParsePackageJSONFile, {
PackageJSON,
PackageJSONVersion
} from './packageParser/loadAndParsePackageJSONFile.js';
import loadAndParsePackageJSONFile from './packageParser/loadAndParsePackageJSONFile.js';
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
import { type DistMetadata, prepareDistMetadata, type SubPathMetadata } from './packageParser/prepareDistMetadata.js';
import { PackageJSON, PackageJSONVersion } from './packageParser/PackageJSON.js';

export { DistMetadata, SubPathMetadata };

Expand Down Expand Up @@ -35,7 +33,7 @@ export default async function packageParser( packageDir: string ): Promise<Packa
return prepareMetadata( packageDir, metadata );
}

async function prepareMetadata( packageDir, metadata: PackageJSON ): Promise<PackageMetadata> {
async function prepareMetadata( packageDir: string, metadata: PackageJSON ): Promise<PackageMetadata> {
const project = normalizePath( packageDir );

return {
Expand Down
72 changes: 72 additions & 0 deletions src/packageParser/PackageJSON.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
export type PackageJSONVersion = `${ number}.${ number }.${ number }`;

export type PackageJSONSubPath = '.' | `.${ string }`;

export interface PackageJSONConditionalExport {
readonly import: string;
readonly types?: string;
readonly requires?: string;
}

export type PackageJSONSubPathExports = Readonly<Record<PackageJSONSubPath, string | PackageJSONConditionalExport>>;

export type PackageJSONExports =
| string
| PackageJSONSubPathExports
| PackageJSONConditionalExport;

export type PackageJSONAuthor = string | {
name: string;
};

export type PackageJSONBin = string | Record<string, string>;

export interface PackageJSONEngines {
readonly node?: string;
}

export interface PackageJSON {
readonly name: string;
readonly version: PackageJSONVersion;
readonly author: PackageJSONAuthor;
readonly license: string;
readonly exports: PackageJSONExports;
readonly bin?: PackageJSONBin;
readonly engines?: PackageJSONEngines;
}

export function isConditionalExport( obj: unknown ): obj is PackageJSONConditionalExport {
if ( obj === undefined || obj === null ) {
return false;
}

const keys = Object.keys( obj );

if ( keys.length === 0 ) {
return false;
}

return keys.every( ( key ) => {
return key === 'import' || key === 'types' || key === 'require';
} );
}

export function isSubPathExports( obj: unknown ): obj is PackageJSONSubPathExports {
if ( obj === undefined || obj === null ) {
return false;
}

const keys = Object.keys( obj );

if ( keys.length === 0 ) {
return false;
}

return keys.every( ( key ) => {
return isSubPath( key );
} );
}

export function isSubPath( value: unknown ): value is PackageJSONSubPath {
return typeof value === 'string' && value.startsWith( '.' );
}
60 changes: 18 additions & 42 deletions src/packageParser/loadAndParsePackageJSONFile.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,6 @@
import { access, readFile } from 'node:fs/promises';
import { resolve as resolvePath } from 'pathe';

export type PackageJSONVersion = `${ number}.${ number }.${ number }`;

type PackageJSONSubPathExport = '.' | `.${ string }`;

interface PackageJSONConditionalExport {
readonly import?: string;
readonly types?: string;
}

type PackageJSONExports =
| string
| Readonly<Record<PackageJSONSubPathExport, string | PackageJSONConditionalExport>>
| PackageJSONConditionalExport;

type PackageJSONAuthor = string | {
name: string;
};

type PackageJSONBin = string | Record<string, string>;

interface PackageJSONEngines {
readonly node?: string;
}

export interface PackageJSON {
readonly name: string;
readonly version: PackageJSONVersion;
readonly author: PackageJSONAuthor;
readonly license: string;
readonly exports: PackageJSONExports;
readonly bin?: PackageJSONBin;
readonly engines?: PackageJSONEngines;
}
import { isConditionalExport, isSubPathExports, PackageJSON } from './PackageJSON.js';

type UnvalidatedPackageJSON = Partial<PackageJSON>;

Expand Down Expand Up @@ -69,14 +36,7 @@ function validatePackageJSON( obj: UnvalidatedPackageJSON ): void {
throw new ReferenceError( 'Package metadata must contain "version" property.' );
}

const isESMEntryPointPresent = obj.exports !== undefined && (
typeof obj.exports === 'string' ||
typeof obj.exports[ '.' ] === 'string' ||
'import' in obj.exports ||
typeof obj.exports[ '.' ]?.import !== 'undefined'
);

if ( !isESMEntryPointPresent ) {
if ( !isESMEntryPointPresent( obj ) ) {
throw new ReferenceError(
'Package metadata must contain at least one of "exports[ \'.\' ].import" and "exports.import" properties ' +
'or the "exports" property must contain the path.'
Expand All @@ -91,3 +51,19 @@ function validatePackageJSON( obj: UnvalidatedPackageJSON ): void {
throw new ReferenceError( 'Package metadata must contain "license" property.' );
}
}

function isESMEntryPointPresent( { exports }: UnvalidatedPackageJSON ): boolean {
if ( typeof exports === 'string' ) {
return true;
}

if ( isConditionalExport( exports ) ) {
return true;
}

if ( isSubPathExports( exports ) ) {
return true;
}

return false;
}
Loading

0 comments on commit 64c828b

Please sign in to comment.