Skip to content

Commit ae8a17b

Browse files
feat: library generator
1 parent 56b7920 commit ae8a17b

File tree

12 files changed

+320
-1
lines changed

12 files changed

+320
-1
lines changed

packages/qwik-nx/generators.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@
2525
"factory": "./src/generators/component/generator",
2626
"schema": "./src/generators/component/schema.json",
2727
"description": "Generating a Qwik component"
28+
},
29+
"library": {
30+
"factory": "./src/generators/library/generator",
31+
"schema": "./src/generators/library/schema.json",
32+
"description": "library generator"
2833
}
2934
}
3035
}

packages/qwik-nx/src/generators/component/generator.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import {
2-
addProjectConfiguration,
32
formatFiles,
43
generateFiles,
54
getProjects,
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './lib/<%- fileName %>'
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"extends": "<%= rootTsConfigPath %>",
3+
"compilerOptions": {
4+
"allowJs": true,
5+
"target": "ES2017",
6+
"module": "ES2020",
7+
"lib": ["es2020", "DOM", "WebWorker", "DOM.Iterable"],
8+
"jsx": "react-jsx",
9+
"jsxImportSource": "@builder.io/qwik",
10+
"forceConsistentCasingInFileNames": true,
11+
"strict": <%= strict %>,
12+
"noImplicitReturns": true,
13+
"noFallthroughCasesInSwitch": true,
14+
"resolveJsonModule": true,
15+
"moduleResolution": "node",
16+
"esModuleInterop": true,
17+
"skipLibCheck": true,
18+
"incremental": true,
19+
"isolatedModules": true,
20+
"outDir": "tmp",
21+
"noEmit": true
22+
},
23+
"files": [],
24+
"include": [],
25+
"references": [
26+
{
27+
"path": "./tsconfig.lib.json"
28+
<% if(hasUnitTests) { %>
29+
},
30+
{
31+
"path": "./tsconfig.spec.json"
32+
<% } %>
33+
}
34+
]
35+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"extends": "./tsconfig.json",
3+
"compilerOptions": {
4+
"outDir": "<%= offsetFromRoot %>dist/out-tsc",
5+
"types": ["node", "vite/client"]
6+
},
7+
"files": [
8+
],
9+
"exclude": [],
10+
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
11+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { defineConfig, mergeConfig, UserConfigExport } from 'vite';
2+
import { qwikVite } from '@builder.io/qwik/optimizer';
3+
import tsconfigPaths from 'vite-tsconfig-paths';
4+
import base from './<%= offsetFromRoot %>/vite.base';
5+
6+
export default defineConfig(() => {
7+
const overrides: UserConfigExport = {
8+
plugins: [qwikVite(), tsconfigPaths()],
9+
root: '<%= projectRoot %>',
10+
mode: 'lib',
11+
build: {
12+
lib: {
13+
entry: './src/index.ts',
14+
},
15+
},
16+
};
17+
return mergeConfig(base, overrides);
18+
});
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
2+
import { Tree, readProjectConfiguration } from '@nrwl/devkit';
3+
4+
import generator from './generator';
5+
import { LibraryGeneratorSchema } from './schema';
6+
7+
describe('library generator', () => {
8+
let appTree: Tree;
9+
const options: LibraryGeneratorSchema = { name: 'test' };
10+
11+
beforeEach(() => {
12+
appTree = createTreeWithEmptyWorkspace();
13+
});
14+
15+
it('should run successfully', async () => {
16+
await generator(appTree, options);
17+
const config = readProjectConfiguration(appTree, 'test');
18+
expect(config).toBeDefined();
19+
});
20+
});
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import {
2+
addDependenciesToPackageJson,
3+
formatFiles,
4+
generateFiles,
5+
GeneratorCallback,
6+
getWorkspaceLayout,
7+
names,
8+
offsetFromRoot,
9+
readProjectConfiguration,
10+
Tree,
11+
updateProjectConfiguration,
12+
} from '@nrwl/devkit';
13+
import { Linter } from '@nrwl/linter';
14+
import { libraryGenerator } from '@nrwl/workspace/generators';
15+
import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial';
16+
import { getRelativePathToRootTsConfig } from '@nrwl/workspace/src/utilities/typescript';
17+
import * as path from 'path';
18+
import { addViteBase } from '../../utils/add-vite-base';
19+
import { vitestVersion } from '../../utils/versions';
20+
import { LibraryGeneratorSchema } from './schema';
21+
import componentGenerator from './../component/generator'
22+
23+
interface NormalizedSchema extends LibraryGeneratorSchema {
24+
projectName: string;
25+
projectRoot: string;
26+
projectDirectory: string;
27+
parsedTags: string[];
28+
offsetFromRoot: string;
29+
}
30+
31+
function normalizeOptions(tree: Tree, schema: LibraryGeneratorSchema): NormalizedSchema {
32+
const name = names(schema.name).fileName;
33+
const projectDirectory = schema.directory ? `${names(schema.directory).fileName}/${name}` : name;
34+
const projectName = projectDirectory.replace(new RegExp('/', 'g'), '-');
35+
const projectRoot = `${getWorkspaceLayout(tree).libsDir}/${projectDirectory}`;
36+
const parsedTags = schema.tags ? schema.tags.split(',').map((s) => s.trim()) : [];
37+
38+
return {
39+
...schema,
40+
projectName,
41+
projectRoot,
42+
projectDirectory,
43+
parsedTags,
44+
offsetFromRoot: offsetFromRoot(projectRoot),
45+
};
46+
}
47+
48+
export default async function (tree: Tree, schema: LibraryGeneratorSchema) {
49+
const options = normalizeOptions(tree, schema);
50+
const tasks: GeneratorCallback[] = [];
51+
52+
addLibrary(tree,options);
53+
54+
const configuration = readProjectConfiguration(tree, options.projectName);
55+
56+
if (options.linter === Linter.EsLint) {
57+
configuration.targets.lint.options.lintFilePatterns = [`${options.projectRoot}/**/*.{ts,tsx,js,jsx}`];
58+
}
59+
60+
if (options.unitTestRunner === 'vitest') {
61+
tasks.push(addDependenciesToPackageJson( tree, { vitest: vitestVersion, }, {} ))
62+
configuration.targets['test'] = {
63+
executor: 'nx:run-commands',
64+
options: {
65+
command: `vitest -c ${options.projectRoot}/vite.config.ts --run`,
66+
},
67+
};
68+
}
69+
70+
updateProjectConfiguration(tree, options.projectName, configuration);
71+
72+
addViteBase(tree);
73+
if (!options.skipFormat) {
74+
await formatFiles(tree);
75+
}
76+
return runTasksInSerial(...tasks);
77+
}
78+
79+
function addLibrary(tree: Tree, options: NormalizedSchema) {
80+
libraryGenerator(tree, {
81+
...options,
82+
unitTestRunner: 'none',
83+
skipBabelrc: true,
84+
skipFormat: true
85+
});
86+
tree.delete(`${options.projectRoot}/src/lib/${options.name}.ts`);
87+
88+
const templateOptions = {
89+
...options,
90+
...names(options.name),
91+
strict: !!options.strict,
92+
hasUnitTests: options.unitTestRunner === 'vitest',
93+
rootTsConfigPath: getRelativePathToRootTsConfig(tree, options.projectRoot),
94+
};
95+
generateFiles(tree, path.join(__dirname, 'files'), options.projectRoot, templateOptions);
96+
componentGenerator(tree, {
97+
name: options.name,
98+
skipTests: options.unitTestRunner !== 'vitest',
99+
style: options.style,
100+
project: options.projectName,
101+
flat: true
102+
})
103+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { Linter } from '@nrwl/linter';
2+
3+
export interface LibraryGeneratorSchema {
4+
name: string;
5+
directory?: string;
6+
skipFormat?: boolean;
7+
style?: 'css' | 'scss' | 'styl' | 'less' | 'none';
8+
tags?: string;
9+
unitTestRunner?: 'vitest' | 'none';
10+
linter?: Linter;
11+
importPath?: string;
12+
strict?: boolean;
13+
setParserOptionsProject?: boolean;
14+
standaloneConfig?: boolean;
15+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
{
2+
"$schema": "http://json-schema.org/schema",
3+
"cli": "nx",
4+
"$id": "Library",
5+
"title": "",
6+
"type": "object",
7+
"properties": {
8+
"name": {
9+
"type": "string",
10+
"description": "Library name.",
11+
"$default": {
12+
"$source": "argv",
13+
"index": 0
14+
},
15+
"x-prompt": "What name would you like to use for the library?",
16+
"pattern": "^[a-zA-Z].*$"
17+
},
18+
"directory": {
19+
"type": "string",
20+
"description": "A directory where the lib is placed."
21+
},
22+
"linter": {
23+
"description": "The tool to use for running lint checks.",
24+
"type": "string",
25+
"enum": ["eslint", "none"],
26+
"default": "eslint"
27+
},
28+
"unitTestRunner": {
29+
"type": "string",
30+
"enum": ["vitest", "none"],
31+
"description": "Test runner to use for unit tests.",
32+
"default": "vitest"
33+
},
34+
"tags": {
35+
"type": "string",
36+
"description": "Add tags to the library (used for linting)."
37+
},
38+
"style": {
39+
"description": "The file extension to be used for style files.",
40+
"type": "string",
41+
"default": "css",
42+
"alias": "s",
43+
"x-prompt": {
44+
"message": "Which stylesheet format would you like to use?",
45+
"type": "list",
46+
"items": [
47+
{
48+
"value": "css",
49+
"label": "CSS"
50+
},
51+
{
52+
"value": "scss",
53+
"label": "SASS(.scss) [ http://sass-lang.com ]"
54+
},
55+
{
56+
"value": "styl",
57+
"label": "Stylus(.styl) [ http://stylus-lang.com ]"
58+
},
59+
{
60+
"value": "less",
61+
"label": "LESS [ http://lesscss.org ]"
62+
},
63+
{
64+
"value": "none",
65+
"label": "none"
66+
}
67+
],
68+
"default": "css"
69+
}
70+
},
71+
"skipFormat": {
72+
"description": "Skip formatting files.",
73+
"type": "boolean",
74+
"default": false
75+
},
76+
"importPath": {
77+
"type": "string",
78+
"description": "The library name used to import it, like `@myorg/my-awesome-lib`."
79+
},
80+
"strict": {
81+
"type": "boolean",
82+
"description": "Whether to enable `tsconfig` strict mode or not.",
83+
"default": true
84+
},
85+
"setParserOptionsProject": {
86+
"type": "boolean",
87+
"description": "Whether or not to configure the ESLint `parserOptions.project` option. We do not do this by default for lint performance reasons.",
88+
"default": false
89+
},
90+
"standaloneConfig": {
91+
"description": "Split the project configuration into `<projectRoot>/project.json` rather than including it inside `workspace.json`.",
92+
"type": "boolean"
93+
}
94+
},
95+
"required": ["name"]
96+
}

0 commit comments

Comments
 (0)