Skip to content

Commit ff543d6

Browse files
ranm8davidfirst
andauthored
Generator Aspect (bit create) (teambit#3978)
Enable creating new components from a pre-defined templates. See teambit#3940 for details. Co-authored-by: David First <[email protected]>
1 parent 424266d commit ff543d6

18 files changed

+485
-228
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { ComponentID } from '@teambit/component-id';
2+
3+
export interface File {
4+
/**
5+
* relative path of the file within the component.
6+
*/
7+
relativePath: string;
8+
9+
/**
10+
* file content
11+
*/
12+
content: string;
13+
14+
/**
15+
* whether this file will be tracked as the main file
16+
*/
17+
isMain?: boolean;
18+
}
19+
20+
export interface GeneratorContext {
21+
/**
22+
* component name of the generating component. e.g. `button` or `use-date`.
23+
* without the scope and the namespace.
24+
*/
25+
componentName: string;
26+
27+
/**
28+
* e.g. `use-date` becomes `useDate`.
29+
* useful when generating the file content, for example for a function name.
30+
*/
31+
componentNameCamelCase: string;
32+
33+
/**
34+
* component id.
35+
* the name is the name+namespace. the scope is the scope entered by --scope flag or the defaultScope
36+
*/
37+
componentId: ComponentID;
38+
}
39+
40+
export interface ComponentTemplate {
41+
/**
42+
* name of the component template. for example: `hook`, `react-component` or `module`.
43+
*/
44+
name: string;
45+
46+
/**
47+
* template function for generating the file of a certain component.,
48+
*/
49+
generateFiles(context: GeneratorContext): File[];
50+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { Command, CommandOptions } from '@teambit/cli';
2+
import chalk from 'chalk';
3+
import { GeneratorMain } from './generator.main.runtime';
4+
5+
export type GeneratorOptions = {
6+
namespace?: string;
7+
aspect?: string;
8+
scope?: string;
9+
};
10+
11+
export class CreateCmd implements Command {
12+
name = 'create <templateName> <componentNames...>';
13+
description = 'create a new component from a template';
14+
shortDescription = '';
15+
alias = '';
16+
loader = true;
17+
group = 'development';
18+
options = [
19+
['n', 'namespace <string>', `sets the component's namespace and nested dirs inside the scope`],
20+
['s', 'scope <string>', `sets the component's scope-name. if not entered, the default-scope will be used`],
21+
['a', 'aspect <string>', 'aspect-id of the template. helpful when multiple aspects use the same template name'],
22+
] as CommandOptions;
23+
24+
constructor(private generator: GeneratorMain) {}
25+
26+
async report([templateName, componentNames]: [string, string[]], options: GeneratorOptions) {
27+
const results = await this.generator.generateComponentTemplate(componentNames, templateName, options);
28+
const title = `the following ${results.length} component(s) were created`;
29+
30+
const componentsData = results
31+
.map((result) => {
32+
const compTitle = `${chalk.bold(result.id.toString())} at ${result.dir}`;
33+
const compFiles = result.files.map((file) => ` ${file}`).join('\n');
34+
return `${compTitle}\n${compFiles}`;
35+
})
36+
.join('\n');
37+
return `${chalk.green(title)}\n\n${componentsData}`;
38+
}
39+
}

scopes/generator/generator/create.cmd.tsx

Lines changed: 0 additions & 47 deletions
This file was deleted.

scopes/generator/generator/create.main.runtime.ts

Lines changed: 0 additions & 13 deletions
This file was deleted.

scopes/generator/generator/create.provider.ts

Lines changed: 0 additions & 22 deletions
This file was deleted.

scopes/generator/generator/create.ts

Lines changed: 0 additions & 115 deletions
This file was deleted.
Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import { Aspect } from '@teambit/harmony';
22

3-
export const CreateAspect = Aspect.create({
3+
export const GeneratorAspect = Aspect.create({
44
id: 'teambit.generator/generator',
5-
dependencies: [],
6-
defaultConfig: {},
75
});
Lines changed: 63 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,69 @@
1-
Create extension enable generating new components by a default template or extensions registered to this extension.
1+
Generator extension enable generating new components by pre-defined templates
2+
3+
### Component location
4+
5+
Component location in the workspace directory tree is defined with the `bit create` command (see below). For example, a component named `ShoppingCart` created in the `acme.shopper` and the namespace `ui` will be generated in the following directory:
26

3-
### Configuration
4-
Add to your config the following:
57
```
6-
"create": {
7-
"template": "your-extension"
8-
}
8+
acme.shopper/ui/shopping-cart
99
```
1010

11-
### How to extend
12-
First, add this extension as a dependency to your extension.
13-
Then, in the provider, register to this extension and provide a function that returns the template files.
14-
Here is an example:
11+
### Component name
12+
13+
When using templates Bit will use CamelCasing for passing the component-name to the template, but generated file structure should be in kebab-case, for better cross-operating-system compatibility of the component.
14+
15+
### Automatically `add` component
16+
17+
Bit should automatically register the new component to the `.bitmap` file with a symmetrical name to the component-location in the workspace.
18+
19+
## Register a template
20+
Any aspect (include envs) can register templates. Each template should have a name and a list of files. Each file has a relative-path to the component-dir and template content. See the `component-template.ts` file for more info about the exact API.
21+
22+
* Component name should be available as a param for the file-content-template.
23+
* TBD: An environment must have a default template (if not set, use first template in array?).
24+
25+
To register a template, use the Generator API: `registerComponentTemplate(templates: ComponentTemplate[])`.
26+
27+
To make the templates of an aspect available on a workspace, they need to be added to the workspace.jsonc. For example:
28+
```json
29+
"teambit.generator/generator": {
30+
"aspects": [
31+
"teambit.harmony/aspect"
32+
]
33+
},
1534
```
16-
create.register({ name: 'extensions/gulp-ts' }, (name) => {
17-
return {
18-
files: [
19-
{ path: `${name}.js`, content: `export default function ${name}() { console.log('hello'); }` },
20-
{ path: `${name}.spec.js`, content: `export default function ${name}() { console.log('hello from spec'); }` }
21-
],
22-
main: `${name}.js`
23-
};
24-
});
35+
In the example above, the aspect `teambit.harmony/aspect` is configured to be available for the generator.
36+
37+
## Show all available templates
38+
39+
Introduce a new command `bit templates`, which groups all available templates by aspects.
40+
41+
## Generate a template from CLI
42+
43+
Introduce a `create` command to use templates.
44+
45+
```sh
46+
bit create <template-name> <component-name...> [--scope | -s] [--namespace | -n] [--aspect | -a]
2547
```
48+
49+
### Args
50+
51+
#### `<component name>`
52+
53+
Name of the component to create. Will be used as the component's dir name and fed to the component template.
54+
55+
**generated file structure should use kebab-case, while the template itself be in camel case**.
56+
57+
### Options
58+
59+
#### `[--scope | -s]`
60+
61+
Sets the component's scope and base directory. If not defined, use the `defaultScope` from `teambit.workspace/workspace` config.
62+
63+
#### `[--namespace | -n]`
64+
65+
Sets the component's namespace and nested dirs inside the scope. If not define, use empty string.
66+
67+
#### `[--aspect | -a]`
68+
69+
Aspect ID that registered this template, required only if there are two templates with the same name from several aspects in the workspace.

0 commit comments

Comments
 (0)