Skip to content

Commit 5ccf1f7

Browse files
committed
feat: support aura/lwc component templates
@W-9191596@
1 parent 11e4255 commit 5ccf1f7

File tree

20 files changed

+295
-73
lines changed

20 files changed

+295
-73
lines changed

packages/plugin-templates/messages/messages.json

+1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
"MissingAuraFolder": "Lightning bundles must have a parent folder named 'aura'.",
6565
"MissingAppname": "Missing required flag:\n -n, --appname APPNAME name of the generated Lightning app\nSee more help with --help\n",
6666
"MissingComponentName": "Missing required flag:\n -n, --componentname COMPONENTNAME name of the generated Lightning component\nSee more help with --help\n",
67+
"MissingLightningComponentTemplate": "Template %s not available for component type %s.",
6768
"MissingLWCFolder": "Lightning bundles must have a parent folder named 'lwc'.",
6869
"MissingEventname": "Missing required flag:\n -n, --eventname EVENTNAME name of the generated Lightning event\nSee more help with --help\n",
6970
"MissingInterfacename": "Missing required flag:\n -n, --interfacename INTERFACENAME name of the generated Lightning interface\nSee more help with --help\n",

packages/plugin-templates/src/commands/force/lightning/component/create.ts

+13-4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { AnyJson } from '@salesforce/ts-types';
1414
import { MessageUtil, TemplateCommand } from '../../../../utils';
1515

1616
const lightningComponentFileSuffix = /.cmp$/;
17+
const lightningWebComponentFileSuffix = /.js$/;
1718
const BUNDLE_TYPE = MessageUtil.get('Component');
1819

1920
export default class LightningComponent extends TemplateCommand {
@@ -53,10 +54,18 @@ export default class LightningComponent extends TemplateCommand {
5354
char: 't',
5455
description: MessageUtil.get('TemplateFlagDescription'),
5556
longDescription: MessageUtil.get('TemplateFlagLongDescription'),
56-
default: 'DefaultLightningCmp',
57-
options: CreateUtil.getCommandTemplatesForFiletype(
58-
lightningComponentFileSuffix,
59-
'lightningcomponent'
57+
default: 'default',
58+
options: Array.from(
59+
new Set([
60+
...CreateUtil.getCommandTemplatesInSubdirs('lightningcomponent', {
61+
subdir: 'aura',
62+
filetype: lightningComponentFileSuffix
63+
}),
64+
...CreateUtil.getCommandTemplatesInSubdirs('lightningcomponent', {
65+
subdir: 'lwc',
66+
filetype: lightningWebComponentFileSuffix
67+
})
68+
]).values()
6069
)
6170
}),
6271
outputdir: flags.string({

packages/plugin-templates/test/commands/force/lightning/component/create.test.ts

+81-4
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,31 @@ describe('Lightning component creation tests:', () => {
5959
);
6060
}
6161
);
62+
63+
test
64+
.withOrg()
65+
.withProject()
66+
.stdout()
67+
.command([
68+
'force:lightning:component:create',
69+
'--componentname',
70+
'foo',
71+
'--outputdir',
72+
'aura',
73+
'--template',
74+
'default'
75+
])
76+
.it(
77+
'should create lightning aura component files from default template in the aura output directory',
78+
ctx => {
79+
assert.file(AuraLightningTestFormatter.fileformatter('foo', 'foo'));
80+
assert.file(path.join('aura', 'foo', 'foo.cmp-meta.xml'));
81+
assert.fileContent(
82+
path.join('aura', 'foo', 'foo.cmp-meta.xml'),
83+
'<AuraDefinitionBundle xmlns="http://soap.sforce.com/2006/04/metadata">'
84+
);
85+
}
86+
);
6287
});
6388

6489
describe('Check lightning aura components creation without -meta.xml file', () => {
@@ -112,12 +137,12 @@ describe('Lightning component creation tests:', () => {
112137
.it(
113138
'should create lightning web component files in the lwc output directory with the internal flag for disabling -meta.xml files',
114139
ctx => {
115-
assert.file(
116-
path.join('lwc', 'internallwctest', 'internallwctest.html')
117-
);
118140
assert.noFile(
119141
path.join('lwc', 'internallwctest', 'internallwctest.js-meta.xml')
120142
);
143+
assert.file(
144+
path.join('lwc', 'internallwctest', 'internallwctest.html')
145+
);
121146
assert.file(
122147
path.join('lwc', 'internallwctest', 'internallwctest.js')
123148
);
@@ -144,9 +169,43 @@ describe('Lightning component creation tests:', () => {
144169
'lwc'
145170
])
146171
.it(
147-
'should create lightning web component files in the lwc output directory with the internal flag for disabling -meta.xml files',
172+
'should create lightning web component files in the lwc output directory',
173+
ctx => {
174+
assert.file(path.join('lwc', 'foo', 'foo.js-meta.xml'));
175+
assert.file(path.join('lwc', 'foo', 'foo.html'));
176+
assert.file(path.join('lwc', 'foo', 'foo.js'));
177+
assert.fileContent(
178+
path.join('lwc', 'foo', 'foo.js'),
179+
'export default class Foo extends LightningElement {}'
180+
);
181+
}
182+
);
183+
184+
test
185+
.withOrg()
186+
.withProject()
187+
.stdout()
188+
.command([
189+
'force:lightning:component:create',
190+
'--componentname',
191+
'foo',
192+
'--outputdir',
193+
'lwc',
194+
'--type',
195+
'lwc',
196+
'--template',
197+
'default'
198+
])
199+
.it(
200+
'should create lightning web component files from default template in the lwc output directory',
148201
ctx => {
149202
assert.file(path.join('lwc', 'foo', 'foo.js-meta.xml'));
203+
assert.file(path.join('lwc', 'foo', 'foo.html'));
204+
assert.file(path.join('lwc', 'foo', 'foo.js'));
205+
assert.fileContent(
206+
path.join('lwc', 'foo', 'foo.js'),
207+
'export default class Foo extends LightningElement {}'
208+
);
150209
}
151210
);
152211
});
@@ -184,5 +243,23 @@ describe('Lightning component creation tests:', () => {
184243
.it('should throw missing lwc parent folder error', ctx => {
185244
expect(ctx.stderr).to.contain(messages.getMessage('MissingLWCFolder'));
186245
});
246+
test
247+
.withOrg()
248+
.withProject()
249+
.stderr()
250+
.command([
251+
'force:lightning:component:create',
252+
'--outputdir',
253+
'lwc',
254+
'--componentname',
255+
'foo',
256+
'--type',
257+
'lwc',
258+
'--template',
259+
'foo'
260+
])
261+
.it('should throw invalid template error', ctx => {
262+
expect(ctx.stderr).to.contain(messages.getMessage('InvalidTemplate'));
263+
});
187264
});
188265
});

packages/templates/src/generators/lightningComponentGenerator.ts

+86-60
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,19 @@ export default class LightningComponentGenerator extends SfdxGenerator<
3030
throw new Error(nls.localize('MissingAuraDir'));
3131
}
3232
}
33+
34+
if (
35+
CreateUtil.getCommandTemplatesInSubdirs('lightningcomponent', {
36+
subdir: this.options.type
37+
}).indexOf(this.options.template) < 0
38+
) {
39+
throw new Error(
40+
nls.localize('MissingLightningComponentTemplate', [
41+
this.options.template,
42+
this.options.type
43+
])
44+
);
45+
}
3346
}
3447

3548
public writing() {
@@ -41,14 +54,21 @@ export default class LightningComponentGenerator extends SfdxGenerator<
4154
type,
4255
internal
4356
} = this.options;
44-
// tslint:disable-next-line:no-unused-expression
57+
4558
if (type === 'aura') {
4659
this.sourceRoot(
47-
path.join(__dirname, '..', 'templates', 'lightningcomponent', 'aura')
60+
path.join(
61+
__dirname,
62+
'..',
63+
'templates',
64+
'lightningcomponent',
65+
'aura',
66+
template
67+
)
4868
);
4969
if (!internal) {
5070
this.fs.copyTpl(
51-
this.templatePath('_auradefinitionbundle.cmp-meta.xml'),
71+
this.templatePath(`${template}.cmp-meta.xml`),
5272
this.destinationPath(
5373
path.join(outputdir, componentname, `${componentname}.cmp-meta.xml`)
5474
),
@@ -60,63 +80,62 @@ export default class LightningComponentGenerator extends SfdxGenerator<
6080
);
6181
}
6282
this.fs.copyTpl(
63-
this.templatePath('DefaultLightningAuradoc.auradoc'),
83+
this.templatePath(`${template}.auradoc`),
6484
this.destinationPath(
6585
path.join(outputdir, componentname, `${componentname}.auradoc`)
6686
),
6787
{}
68-
),
69-
this.fs.copyTpl(
70-
this.templatePath(`${template}.cmp`),
71-
this.destinationPath(
72-
path.join(outputdir, componentname, `${componentname}.cmp`)
73-
),
74-
{}
88+
);
89+
this.fs.copyTpl(
90+
this.templatePath(`${template}.cmp`),
91+
this.destinationPath(
92+
path.join(outputdir, componentname, `${componentname}.cmp`)
7593
),
76-
this.fs.copyTpl(
77-
this.templatePath('DefaultLightningCss.css'),
78-
this.destinationPath(
79-
path.join(outputdir, componentname, `${componentname}.css`)
80-
),
81-
{}
94+
{}
95+
);
96+
this.fs.copyTpl(
97+
this.templatePath(`${template}.css`),
98+
this.destinationPath(
99+
path.join(outputdir, componentname, `${componentname}.css`)
82100
),
83-
this.fs.copyTpl(
84-
this.templatePath('DefaultLightningDesign.design'),
85-
this.destinationPath(
86-
path.join(outputdir, componentname, `${componentname}.design`)
87-
),
88-
{}
101+
{}
102+
);
103+
this.fs.copyTpl(
104+
this.templatePath(`${template}.design`),
105+
this.destinationPath(
106+
path.join(outputdir, componentname, `${componentname}.design`)
89107
),
90-
this.fs.copyTpl(
91-
this.templatePath('DefaultLightningSVG.svg'),
92-
this.destinationPath(
93-
path.join(outputdir, componentname, `${componentname}.svg`)
94-
),
95-
{}
108+
{}
109+
);
110+
this.fs.copyTpl(
111+
this.templatePath(`${template}.svg`),
112+
this.destinationPath(
113+
path.join(outputdir, componentname, `${componentname}.svg`)
96114
),
97-
this.fs.copyTpl(
98-
this.templatePath('DefaultLightningController.js'),
99-
this.destinationPath(
100-
path.join(outputdir, componentname, `${componentname}Controller.js`)
101-
),
102-
{}
115+
{}
116+
);
117+
this.fs.copyTpl(
118+
this.templatePath(`${template}Controller.js`),
119+
this.destinationPath(
120+
path.join(outputdir, componentname, `${componentname}Controller.js`)
103121
),
104-
this.fs.copyTpl(
105-
this.templatePath('DefaultLightningHelper.js'),
106-
this.destinationPath(
107-
path.join(outputdir, componentname, `${componentname}Helper.js`)
108-
),
109-
{}
122+
{}
123+
);
124+
this.fs.copyTpl(
125+
this.templatePath(`${template}Helper.js`),
126+
this.destinationPath(
127+
path.join(outputdir, componentname, `${componentname}Helper.js`)
110128
),
111-
this.fs.copyTpl(
112-
this.templatePath('DefaultLightningRenderer.js'),
113-
this.destinationPath(
114-
path.join(outputdir, componentname, `${componentname}Renderer.js`)
115-
),
116-
{}
117-
);
129+
{}
130+
);
131+
this.fs.copyTpl(
132+
this.templatePath(`${template}Renderer.js`),
133+
this.destinationPath(
134+
path.join(outputdir, componentname, `${componentname}Renderer.js`)
135+
),
136+
{}
137+
);
118138
}
119-
// tslint:disable-next-line:no-unused-expression
120139
if (type === 'lwc') {
121140
// lwc requires first letter of filename to be lowercase
122141
const fileName = `${componentname
@@ -129,23 +148,30 @@ export default class LightningComponentGenerator extends SfdxGenerator<
129148
.toUpperCase()}${componentname.substring(1)}`;
130149

131150
this.sourceRoot(
132-
path.join(__dirname, '..', 'templates', 'lightningcomponent', 'lwc')
151+
path.join(
152+
__dirname,
153+
'..',
154+
'templates',
155+
'lightningcomponent',
156+
'lwc',
157+
template
158+
)
133159
);
134160
this.fs.copyTpl(
135-
this.templatePath('DefaultLightningLWC.js'),
161+
this.templatePath(`${template}.js`),
136162
this.destinationPath(path.join(outputdir, fileName, `${fileName}.js`)),
137163
{ className }
138-
),
139-
this.fs.copyTpl(
140-
this.templatePath('_.html'),
141-
this.destinationPath(
142-
path.join(outputdir, fileName, `${fileName}.html`)
143-
),
144-
{}
145-
);
164+
);
165+
this.fs.copyTpl(
166+
this.templatePath(`${template}.html`),
167+
this.destinationPath(
168+
path.join(outputdir, fileName, `${fileName}.html`)
169+
),
170+
{}
171+
);
146172
if (!internal) {
147173
this.fs.copyTpl(
148-
this.templatePath('_js-meta.xml'),
174+
this.templatePath(`${template}.js-meta.xml`),
149175
this.destinationPath(
150176
path.join(outputdir, fileName, `${fileName}.js-meta.xml`)
151177
),

packages/templates/src/i18n/i18n.ts

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ export const messages = {
2626
"Analytics templates must have a parent folder named 'waveTemplates'.",
2727
MissingAuraDir: "Lightning bundles must have a parent folder named 'aura'.",
2828
MissingLWCDir: "Lightning bundles must have a parent folder named 'lwc'.",
29+
MissingLightningComponentTemplate:
30+
'Template %s not available for component type %s.',
2931

3032
LightningAppBundle: 'A Lightning Application Bundle',
3133
LightningComponentBundle: 'A Lightning Component Bundle',

packages/templates/src/utils/createUtil.ts

+29
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,33 @@ export class CreateUtil {
4848
});
4949
return files;
5050
}
51+
52+
/** Get the names of directories that contain matching template files.
53+
* This will look in directories under the command/subdir folder.
54+
* @param command the command name
55+
* @param filetype optional file name pattern to match on in the subdirectories
56+
* @param subdir optional subdirectory under `templates/${command}`
57+
* @return the set of template names
58+
*/
59+
public static getCommandTemplatesInSubdirs(
60+
command: string,
61+
{ filetype, subdir }: { filetype?: RegExp; subdir?: string } = {}
62+
): string[] {
63+
let basedir = path.resolve(__dirname, '..', 'templates', command);
64+
if (subdir) {
65+
basedir = path.join(basedir, subdir);
66+
}
67+
const subdirs = fs
68+
.readdirSync(basedir, { withFileTypes: true })
69+
.filter(ent => ent.isDirectory())
70+
.map(ent => ent.name);
71+
if (filetype) {
72+
return subdirs.filter(dir =>
73+
fs
74+
.readdirSync(path.join(basedir, dir), { withFileTypes: true })
75+
.some(ent => ent.isFile() && filetype.test(ent.name))
76+
);
77+
}
78+
return subdirs;
79+
}
5180
}

0 commit comments

Comments
 (0)