Skip to content

Commit

Permalink
feat(plugin): add plugin for generators (#26)
Browse files Browse the repository at this point in the history
closes #4

This PR adds:
- `plugin` for the main `ngxtension` plugin that provides generators such as `ng add` (at the moment)
- `local-plugin` for various tasks in the repo itself. Right now, it has `convert-entry-point-to-project` generator
  • Loading branch information
nartc committed Sep 13, 2023
1 parent 1d70792 commit 554a35c
Show file tree
Hide file tree
Showing 47 changed files with 1,700 additions and 245 deletions.
5 changes: 5 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
"ignorePatterns": ["**/*"],
"plugins": ["@nx"],
"overrides": [
{
"files": "*.json",
"parser": "jsonc-eslint-parser",
"rules": {}
},
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {
Expand Down
2 changes: 1 addition & 1 deletion .release-it.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"releaseName": "Release ${version}"
},
"hooks": {
"before:bump": "pnpm exec nx build ngxtension",
"before:bump": "pnpm exec nx package ngxtension",
"after:bump": ["git checkout -- package.json"]
}
}
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"eslint.validate": ["json"]
}
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@ A collection of utilities for [Angular](https://angular.io)

## Installation

The `ng-add` schematic/generator installs the `ngxtension` package as well as turning on `skipLibCheck` TypeScript configuration if it hasn't been turned on.
This allows your project to skip checking types for external libraries like `ngxtension` where typings might not be compatible with your project's strictness.

```shell
ng add ngxtension
```

or

```shell
npm install ngxtension
```
Expand All @@ -22,6 +31,10 @@ yarn add ngxtension
pnpm install ngxtension
```

```shell
nx generate ngxtension:init
```

## Utilities

<!-- UTILITIES:START -->
Expand Down
32 changes: 32 additions & 0 deletions libs/local-plugin/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"extends": ["../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.json"],
"parser": "jsonc-eslint-parser",
"rules": {
"@nx/dependency-checks": "error"
}
},
{
"files": ["./package.json", "./generators.json"],
"parser": "jsonc-eslint-parser",
"rules": {
"@nx/nx-plugin-checks": "error"
}
}
]
}
11 changes: 11 additions & 0 deletions libs/local-plugin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# local-plugin

This library was generated with [Nx](https://nx.dev).

## Building

Run `nx build local-plugin` to build the library.

## Running unit tests

Run `nx test local-plugin` to execute the unit tests via [Jest](https://jestjs.io).
9 changes: 9 additions & 0 deletions libs/local-plugin/generators.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"generators": {
"convert-entry-point-to-project": {
"factory": "./src/generators/convert-entry-point-to-project/generator",
"schema": "./src/generators/convert-entry-point-to-project/schema.json",
"description": "convert-entry-point-to-project generator"
}
}
}
10 changes: 10 additions & 0 deletions libs/local-plugin/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* eslint-disable */
export default {
displayName: 'local-plugin',
preset: '../../jest.preset.js',
transform: {
'^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
},
moduleFileExtensions: ['ts', 'js', 'html'],
coverageDirectory: '../../coverage/libs/local-plugin',
};
12 changes: 12 additions & 0 deletions libs/local-plugin/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "local-plugin",
"version": "0.0.1",
"dependencies": {
"@nx/devkit": "16.8.1",
"tslib": "^2.3.0"
},
"type": "commonjs",
"main": "./src/index.js",
"typings": "./src/index.d.ts",
"generators": "./generators.json"
}
66 changes: 66 additions & 0 deletions libs/local-plugin/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
{
"name": "local-plugin",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "libs/local-plugin/src",
"projectType": "library",
"targets": {
"build": {
"executor": "@nx/js:tsc",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/libs/local-plugin",
"main": "libs/local-plugin/src/index.ts",
"tsConfig": "libs/local-plugin/tsconfig.lib.json",
"assets": [
"libs/local-plugin/*.md",
{
"input": "./libs/local-plugin/src",
"glob": "**/!(*.ts)",
"output": "./src"
},
{
"input": "./libs/local-plugin/src",
"glob": "**/*.d.ts",
"output": "./src"
},
{
"input": "./libs/local-plugin",
"glob": "generators.json",
"output": "."
},
{
"input": "./libs/local-plugin",
"glob": "executors.json",
"output": "."
}
]
}
},
"lint": {
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": [
"libs/local-plugin/**/*.ts",
"libs/local-plugin/package.json",
"libs/local-plugin/generators.json"
]
}
},
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "libs/local-plugin/jest.config.ts",
"passWithNoTests": true
},
"configurations": {
"ci": {
"ci": true,
"codeCoverage": true
}
}
}
},
"tags": []
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import {
UnitTestRunner,
libraryGenerator,
librarySecondaryEntryPointGenerator,
} from '@nx/angular/generators';
import { Tree, readProjectConfiguration } from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import initGenerator from './generator';

const project = 'main-entry';
const entryPoint = 'entry-point';

describe('convert-entry-point-to-project generator', () => {
let tree: Tree;

beforeEach(() => {
tree = createTreeWithEmptyWorkspace();
});

async function setup(isReady = false) {
tree = createTreeWithEmptyWorkspace();

await libraryGenerator(tree, {
name: project,
publishable: true,
importPath: project,
routing: false,
spec: false,
skipTests: true,
skipModule: true,
unitTestRunner: UnitTestRunner.None,
});

if (isReady) {
await librarySecondaryEntryPointGenerator(tree, {
name: entryPoint,
library: project,
skipModule: true,
});
}
}

//
it('should run successfully', async () => {
expect(true).toBeTruthy();
});

it('should fail fast for name == "src"', async () => {
await setup();
await initGenerator(tree, { name: 'src', project });
expect(nothingHappened(tree)).toEqual(true);
});

it('should throw for a non project', async () => {
await setup();
await expect(
initGenerator(tree, { name: entryPoint, project: 'any' })
).rejects.toThrowError(/cannot find configuration for 'any'/i);
});

it('should work properly', async () => {
await setup(true);
await initGenerator(tree, { name: entryPoint, project });
expect(nothingHappened(tree)).toEqual(false);
});
});

function nothingHappened(tree: Tree) {
const projectConfiguration = readProjectConfiguration(tree, project);
return !tree.exists(
projectConfiguration.root + '/' + entryPoint + '/project.json'
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import {
Tree,
addProjectConfiguration,
formatFiles,
getWorkspaceLayout,
logger,
readJson,
readProjectConfiguration,
updateJson,
} from '@nx/devkit';
import type { ConvertEntryPointToProjectGeneratorSchema } from './schema';

export async function convertEntryPointToProjectGenerator(
tree: Tree,
options: ConvertEntryPointToProjectGeneratorSchema
) {
const { name, project } = options;
if (name === 'src') {
logger.warn(`[local-plugin] entry point "src" is invalid`);
return;
}

const projectConfiguration = readProjectConfiguration(tree, project);

const projectPackageJson = readJson(
tree,
projectConfiguration.root + '/package.json'
);

if (!projectPackageJson.name) {
logger.error(
`[local-plugin] project ${project} does not have a name in package.json`
);
return;
}

const { libsDir } = getWorkspaceLayout(tree);

const entryPointPath = libsDir
? `${libsDir}/${project}/${name}`
: `${project}/${name}`;

const isExist = tree.exists(entryPointPath);
if (!isExist) {
logger.error(`[local-plugin] ${name} not found as an entry point`);
return;
}

const isProjectJsonExist = tree.exists(`${entryPointPath}/project.json`);
if (isProjectJsonExist) {
logger.info(`[local-plugin] ${name} entry point is already a Project`);
return;
}

addProjectConfiguration(tree, `${projectPackageJson.name}/${name}`, {
root: entryPointPath,
projectType: 'library',
sourceRoot: `${entryPointPath}/src`,
targets: {
test: {
executor: '@nx/jest:jest',
outputs: ['{workspaceRoot}/coverage/{projectRoot}'],
options: {
jestConfig: `${projectConfiguration.root}/jest.config.ts`,
passWithNoTests: true,
},
configurations: {
ci: {
ci: true,
codeCoverage: true,
},
},
},
lint: {
executor: '@nx/linter:eslint',
outputs: ['{options.outputFile}'],
options: {
lintFilePatterns: [
`${entryPointPath}/**/*.ts`,
`${entryPointPath}/**/*.html`,
],
},
},
},
});

updateJson(tree, `${projectConfiguration.root}/project.json`, (json) => {
if (json.targets?.lint?.options?.lintFilePatterns) {
json.targets.lint.options.lintFilePatterns =
json.targets.lint.options.lintFilePatterns.filter(
(pattern: string) => !pattern.includes(entryPointPath)
);
}
return json;
});

await formatFiles(tree);
}

export default convertEntryPointToProjectGenerator;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface ConvertEntryPointToProjectGeneratorSchema {
name: string;
project: string;
}

0 comments on commit 554a35c

Please sign in to comment.