Skip to content

Commit

Permalink
feat: discover entry points from user packages (#383)
Browse files Browse the repository at this point in the history
Closes: #190

BREAKING CHANGES: Discovery of primary and secondary entry points is changed to read from the following file sources. File locations are tried in this order:
 - `package.json` with `ngPackage` property
 - `ng-package.json` (requires a `package.json` as sibling)
 - `ng-package.js` (with a default export, requires a `package.json` as sibling)
  • Loading branch information
alan-agius4 authored and dherges committed Dec 11, 2017
1 parent 31ffc53 commit 4a7e96e
Show file tree
Hide file tree
Showing 17 changed files with 200 additions and 84 deletions.
6 changes: 6 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@
"program": "${workspaceRoot}/integration/samples.dev.js",
"args": [ "material" ]
},
{
"type": "node",
"request": "launch",
"name": "sample: package-js",
"program": "${workspaceRoot}/integration/samples/package-js/build.dev.js"
},
{
"type": "node",
"request": "launch",
Expand Down
38 changes: 26 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,33 +86,47 @@ What about [ng-packagr alongside Nx Workspace](https://github.com/dherges/nx-pac

#### Configuration Locations

Configuration is picked up from the cli `-p` parameter, then from the default location for `ng-package.json`, then from `package.json`.
Configuration is picked up from the project file given by the `-p` CLI option.
The `-p `option may refer to a `package.json` (with custom `ngPackage` property), an `ng-package.json`, or an `ng-package.js` file.
When the `-p` option refers to a directory, the configuration is picked up from the first matching source;
locations are tried in the above-mentioned order.

To configure with a `ng-package.json`, put the `package.json` of the library in the same folder next to the `ng-package.json`.
Contents of `ng-package.json` are for example:
To configure with a `package.json`, put the configuration in the `ngPackage` custom property:

```json
{
"$schema": "./node_modules/ng-packagr/ng-package.schema.json",
"lib": {
"entryFile": "public_api.ts"
"$schema": "./node_modules/ng-packagr/package.schema.json",
"ngPackage": {
"lib": {
"entryFile": "public_api.ts"
}
}
}
```

To configure with a `package.json`, put the configuration in the `ngPackage` custom property:
To configure with a `ng-package.json` or `ng-package.js`, keep the library's `package.json` in the same folder next to `ng-package.json` or `ng-package.js`.

Example of `ng-package.json`:

```json
{
"$schema": "./node_modules/ng-packagr/package.schema.json",
"ngPackage": {
"lib": {
"entryFile": "public_api.ts"
}
"$schema": "./node_modules/ng-packagr/ng-package.schema.json",
"lib": {
"entryFile": "public_api.ts"
}
}
```

Example of `ng-package.js`:

```js
module.exports = {
lib: {
entryFile: 'public_api.ts'
}
};
```

Note: referencing the `$schema` enables JSON editing support (auto-completion for configuration) in IDEs like [VSCode](https://github.com/Microsoft/vscode).

#### Secondary Entry Points
Expand Down
15 changes: 10 additions & 5 deletions integration/samples.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@ set -e

for D in `find ./integration/samples/* -maxdepth 0 -type d`
do
P=${D}/ng-package.json
if [ -f $P ]; then
echo "Building sample ${P}..."
node dist/cli/main.js -p ${P}
P_JS=${D}/ng-package.js
P_JSON=${D}/ng-package.json
P=${D}/package.json

if [ -f $P_JS ]; then
echo "Building sample ${P_JS}..."
node dist/cli/main.js -p ${P_JS}
elif [ -f $P_JSON ]; then
echo "Building sample ${P_JSON}..."
node dist/cli/main.js -p ${P_JSON}
else
P=${D}/package.json
echo "Building sample ${P}..."
node dist/cli/main.js -p ${P}
fi
Expand Down
4 changes: 4 additions & 0 deletions integration/samples/package-js/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Sample library: Configuration in ng-package.js
======================================

The configuration in this sample resides inside `ng-package.js`.
8 changes: 8 additions & 0 deletions integration/samples/package-js/baz/baz.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Component } from '@angular/core';

@Component({
selector: 'baz-component',
template: '<h1>hello world</h1>'
})
export class BazComponent {
}
13 changes: 13 additions & 0 deletions integration/samples/package-js/build.dev.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const path = require('path');
process.env.DEBUG = true;

// @see https://github.com/TypeStrong/ts-node#programmatic-usage
require('ts-node').register({
project: path.join(__dirname, '..', '..', '..', 'tsconfig.packagr.json')
});

const ngPackagr = require('../../../src/lib/ng-packagr');

ngPackagr.createNgPackage({
project: path.resolve(__dirname, 'ng-package.js')
});
2 changes: 2 additions & 0 deletions integration/samples/package-js/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './baz/baz.component';
export * from './ui-lib.module';
5 changes: 5 additions & 0 deletions integration/samples/package-js/ng-package.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
lib: {
entryFile: 'index.ts'
}
};
12 changes: 12 additions & 0 deletions integration/samples/package-js/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"$schema": "../../../src/package.schema.json",
"name": "@sample/package-js",
"description": "A sample library with configuration in ng-package.js",
"version": "1.0.0-pre.0",
"private": true,
"repository": "https://github.com/dherges/ng-packagr.git",
"peerDependencies": {
"@angular/core": "^4.1.2",
"@angular/common": "^4.1.2"
}
}
8 changes: 8 additions & 0 deletions integration/samples/package-js/secondary/baz/baz.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Component } from '@angular/core';

@Component({
selector: 'baz-component',
template: '<h1>hello world</h1>'
})
export class BazComponent {
}
2 changes: 2 additions & 0 deletions integration/samples/package-js/secondary/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './baz/baz.component';
export * from './ui-lib.module';
2 changes: 2 additions & 0 deletions integration/samples/package-js/secondary/ng-package.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
const config = require('../ng-package');
module.exports = config;
4 changes: 4 additions & 0 deletions integration/samples/package-js/secondary/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"$schema": "../../../../src/package.schema.json",
"name": "@sample/package-js-secondary"
}
13 changes: 13 additions & 0 deletions integration/samples/package-js/secondary/ui-lib.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { NgModule } from '@angular/core';

import { BazComponent } from './baz/baz.component';

@NgModule({
declarations: [
BazComponent,
],
exports: [
BazComponent,
]
})
export class UiLibModule {}
29 changes: 29 additions & 0 deletions integration/samples/package-js/specs/package.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { expect } from 'chai';
import * as fs from 'fs';
import * as path from 'path';

describe(`@sample/package-js`, () => {

describe(`package.json`, () => {
let PACKAGE;
before(() => {
PACKAGE = require('../dist/package.json');
});

it(`should exist`, () => {
expect(PACKAGE).to.be.ok;
});
});

describe(`secondary/package.json`, () => {
let PACKAGE;
before(() => {
PACKAGE = require('../dist/secondary/package.json');
});

it(`should exist`, () => {
expect(PACKAGE).to.be.ok;
});
});

});
13 changes: 13 additions & 0 deletions integration/samples/package-js/ui-lib.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { NgModule } from '@angular/core';

import { BazComponent } from './baz/baz.component';

@NgModule({
declarations: [
BazComponent,
],
exports: [
BazComponent,
]
})
export class UiLibModule {}
110 changes: 43 additions & 67 deletions src/lib/steps/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import * as log from '../util/log';
import { ensureUnixPath } from '../util/path';
import { NgPackageConfig } from '../../ng-package.schema';
import { NgPackage, NgEntryPoint } from '../domain/ng-package-format';
import { debug } from '../util/log';

/** Creates a SchemaClass for `NgPackageConfig` */
const NgPackageSchemaClass =
Expand Down Expand Up @@ -33,73 +34,48 @@ interface UserPackage {
*/
const resolvePackageConf =
async (folderPathOrFilePath: string): Promise<UserPackage> => {

const pathStats = await lstat(folderPathOrFilePath);
if (pathStats.isDirectory()) {
const basePath = path.isAbsolute(folderPathOrFilePath) ? folderPathOrFilePath : path.resolve(folderPathOrFilePath);
const packageJson = await readJson(path.resolve(folderPathOrFilePath, 'package.json'));

if (packageJson['ngPackage']) {
log.debug(`Found package file ${folderPathOrFilePath}`);

return {
packageJson: packageJson,
ngPackageJson: Object.assign({}, packageJson['ngPackage']),
basePath
};
} else if (await fileExists(path.resolve(basePath, 'ng-package.json'))) {

return {
packageJson: packageJson,
ngPackageJson: await readJson(path.resolve(basePath, 'ng-package.json')),
basePath
};
} else if ((await fileExists(path.resolve(basePath, 'ng-package.js')))) {

// TODO: enable this, see #278
if (4 == 4) {
throw new Error(`Reading 'ngPackage' from a .js file is not yet implemented.`);
}
}
const fullPath = path.isAbsolute(folderPathOrFilePath) ? folderPathOrFilePath : path.resolve(folderPathOrFilePath);
const basePath = pathStats.isDirectory() ? fullPath : path.dirname(fullPath);

} else if (pathStats.isFile()) {

const fileName = path.basename(folderPathOrFilePath);
const folderPath = path.dirname(folderPathOrFilePath);
if (fileName=== 'ng-package.json') {

return {
packageJson: await readJson(path.resolve(folderPath, 'package.json')),
ngPackageJson: await readJson(folderPathOrFilePath),
basePath: folderPath
};
} else if (fileName === 'package.json') {
const packageJson = await readJson(folderPathOrFilePath);
if (!packageJson['ngPackage']) {
throw new Error(`Cannot read a package from 'package.json' without 'ngPackage' property.`);
}
const packageJson = await readJson(path.join(basePath, 'package.json'));
const packageConfPathJson = path.join(basePath, 'ng-package.json');
const packageConfPathJs = path.join(basePath, 'ng-package.js');

return {
packageJson: packageJson,
ngPackageJson: Object.assign({}, packageJson['ngPackage']),
basePath: folderPath
};
} else if (path.extname(folderPathOrFilePath) === '.js') {
let packageConf: UserPackage | undefined = {
basePath,
packageJson,
ngPackageJson: {}
};

if (packageJson['ngPackage']) {
packageConf.ngPackageJson = { ...packageJson['ngPackage'] }
} else if (await fileExists(packageConfPathJson)) {
packageConf.ngPackageJson = await readJson(packageConfPathJson);
} else if ((await fileExists(packageConfPathJs))) {
packageConf.ngPackageJson = await import(packageConfPathJs);
} else {
packageConf = undefined;
}

// TODO: enable this, see #278
if (4 == 4) {
throw new Error(`Reading 'ngPackage' from a .js file is not yet implemented.`);
}
if (packageConf || pathStats.isDirectory()) {
// return even if it's undefined and use defaults when it's not a file
return packageConf;
}

} else {
throw new Error(`Trying to read a package from unsupported file extension. Path=${folderPathOrFilePath}`);
if (pathStats.isFile()) {
// a project file was specified but was in valid
if (path.basename(folderPathOrFilePath) === "package.json") {
throw new Error(`Cannot read a package from 'package.json' without 'ngPackage' property.`);
}

throw new Error(`Trying to read a package from unsupported file extension. Path=${folderPathOrFilePath}`);
}

throw new Error(`Cannot discover package sources at ${folderPathOrFilePath}`);
}


/** Reads a primary entry point from it's package file. */
const primaryEntryPoint =
({ packageJson, ngPackageJson, basePath }: UserPackage): NgEntryPoint =>
Expand All @@ -125,12 +101,12 @@ const findSecondaryPackagesPaths =
'.ng_build',
'.ng_pkg_build',
]
.map((directoryName) => `**/${directoryName}/**/package.json`)
.concat([
path.resolve(directoryPath, 'package.json'),
path.resolve(directoryPath, 'ng-package.json'),
path.resolve(directoryPath, excludeFolder) + '/**/package.json'
]);
.map((directoryName) => `**/${directoryName}/**/package.json`)
.concat([
path.resolve(directoryPath, 'package.json'),
path.resolve(directoryPath, 'ng-package.json'),
path.resolve(directoryPath, excludeFolder) + '/**/package.json'
]);

return new Promise<string[]>((resolve, reject) => {
glob(`${directoryPath}/**/*package.json`,
Expand All @@ -144,11 +120,11 @@ const findSecondaryPackagesPaths =
}
);
})
.then((filePaths) => Promise.all(
filePaths
.map((filePath) => path.dirname(filePath))
.filter((value, index, array) => array.indexOf(value) === index)
));
.then((filePaths) => Promise.all(
filePaths
.map((filePath) => path.dirname(filePath))
.filter((value, index, array) => array.indexOf(value) === index)
));
}

/**
Expand All @@ -158,7 +134,7 @@ const findSecondaryPackagesPaths =
* @param primary The primary entry point.
*/
const secondaryEntryPoint =
(primaryDirectoryPath: string, primary: NgEntryPoint, { packageJson, ngPackageJson, basePath}: UserPackage) : NgEntryPoint => {
(primaryDirectoryPath: string, primary: NgEntryPoint, { packageJson, ngPackageJson, basePath }: UserPackage): NgEntryPoint => {

if (basePath === primaryDirectoryPath) {
log.error(`Cannot read secondary entry point. It's already a primary entry point. path=${basePath}`);
Expand Down

0 comments on commit 4a7e96e

Please sign in to comment.