55 * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
66 */
77
8- import fs from 'node:fs' ;
98import path from 'node:path' ;
109import { SfCommand , Flags } from '@salesforce/sf-plugins-core' ;
11- import { Messages } from '@salesforce/core' ;
10+ import { Messages , SfProject } from '@salesforce/core' ;
1211import { cmpDev } from '@lwrjs/api' ;
12+ import { ComponentUtils } from '../../../shared/componentUtils.js' ;
1313import { PromptUtils } from '../../../shared/promptUtils.js' ;
1414
1515Messages . importMessagesDirectoryFromMetaUrl ( import . meta. url ) ;
1616const messages = Messages . loadMessages ( '@salesforce/plugin-lightning-dev' , 'lightning.dev.component' ) ;
1717
18- // TODO support other module directories
19- const MODULES_DIR = path . resolve ( path . join ( 'force-app' , 'main' , 'default' , 'lwc' ) ) ;
20-
21- function getDirectories ( filePath : string ) : string [ ] {
22- try {
23- const items = fs . readdirSync ( filePath ) ;
24-
25- const directories = items . filter ( ( item ) => fs . statSync ( path . join ( filePath , item ) ) . isDirectory ( ) ) ;
26-
27- return directories ;
28- } catch ( error ) {
29- return [ ] ;
30- }
31- }
32-
3318export default class LightningDevComponent extends SfCommand < void > {
3419 public static readonly summary = messages . getMessage ( 'summary' ) ;
3520 public static readonly description = messages . getMessage ( 'description' ) ;
@@ -48,35 +33,56 @@ export default class LightningDevComponent extends SfCommand<void> {
4833
4934 public async run ( ) : Promise < void > {
5035 const { flags } = await this . parse ( LightningDevComponent ) ;
36+ const project = await SfProject . resolve ( ) ;
5137
52- let name = flags . name ;
53- if ( ! name ) {
54- const dirs = getDirectories ( path . resolve ( MODULES_DIR ) ) ;
55- if ( ! dirs ) {
56- throw new Error ( messages . getMessage ( 'error.directory' ) ) ;
57- }
58-
59- const components = dirs . map ( ( dir ) => {
60- const xmlPath = path . resolve ( path . join ( MODULES_DIR , dir , `${ dir } .js-meta.xml` ) ) ;
61- const xmlContent = fs . readFileSync ( xmlPath , 'utf-8' ) ;
62- const label = xmlContent . match ( / < m a s t e r L a b e l > ( .* ?) < \/ m a s t e r L a b e l > / ) ;
63- const description = xmlContent . match ( / < d e s c r i p t i o n > ( .* ?) < \/ d e s c r i p t i o n > / ) ;
38+ const namespacePaths = await ComponentUtils . getNamespacePaths ( project ) ;
39+ const componentPaths = await ComponentUtils . getAllComponentPaths ( namespacePaths ) ;
40+ if ( ! componentPaths ) {
41+ throw new Error ( messages . getMessage ( 'error.directory' ) ) ;
42+ }
6443
65- return {
66- name : dir ,
67- label : label ? label [ 1 ] : '' ,
68- description : description ? description [ 1 ] : '' ,
69- } ;
70- } ) ;
44+ const components = (
45+ await Promise . all (
46+ componentPaths . map ( async ( componentPath ) => {
47+ let xml ;
48+
49+ try {
50+ xml = await ComponentUtils . getComponentMetadata ( componentPath ) ;
51+ } catch ( err ) {
52+ this . warn ( messages . getMessage ( 'error.component-metadata' , [ componentPath ] ) ) ;
53+ }
54+
55+ // components must have meta xml to be previewed
56+ if ( ! xml ) {
57+ return undefined ;
58+ }
59+
60+ const componentName = path . basename ( componentPath ) ;
61+ const label = ComponentUtils . componentNameToTitleCase ( componentName ) ;
62+
63+ return {
64+ name : componentName ,
65+ label : xml . LightningComponentBundle . masterLabel ?? label ,
66+ description : xml . LightningComponentBundle . description ?? '' ,
67+ } ;
68+ } )
69+ )
70+ ) . filter ( ( component ) => ! ! component ) ;
7171
72+ let name = flags . name ;
73+ if ( name ) {
74+ // validate that the component exists before launching the server
75+ if ( ! components . find ( ( component ) => name === component . name ) ) {
76+ throw new Error ( messages . getMessage ( 'error.component-not-found' , [ name ] ) ) ;
77+ }
78+ } else {
79+ // prompt the user for a name if one was not provided
7280 name = await PromptUtils . promptUserToSelectComponent ( components ) ;
7381 if ( ! name ) {
7482 throw new Error ( messages . getMessage ( 'error.component' ) ) ;
7583 }
7684 }
7785
78- this . log ( 'Starting application on port 3000...' ) ;
79-
8086 const port = parseInt ( process . env . PORT ?? '3000' , 10 ) ;
8187
8288 await cmpDev ( {
0 commit comments