Skip to content

Commit

Permalink
fix: failure when missing aura folder (#5718)
Browse files Browse the repository at this point in the history
* fix: fix failure when missing aura folder

@W-16285103@
no logner fails w/ aura or lwc no present
add dup checks across component create

* chore: wip

* chore: wip

* chore: fix a few remaing tidbits

* chore: remove unneeded todo comments

---------

Co-authored-by: Cristina Cañizales <113132642+CristiCanizales@users.noreply.github.com>
  • Loading branch information
peternhale and CristiCanizales authored Aug 12, 2024
1 parent b84e08c commit b7f78db
Show file tree
Hide file tree
Showing 15 changed files with 394 additions and 158 deletions.
22 changes: 0 additions & 22 deletions packages/salesforcedx-sobjects-faux-generator/src/utils/utils.ts

This file was deleted.

1 change: 1 addition & 0 deletions packages/salesforcedx-utils-vscode/src/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export { TraceFlagsRemover } from './traceFlagsRemover';
export {
asyncFilter,
extractJsonObject,
getMessageFromError,
isNullOrUndefined,
fileUtils
} from './utils';
Expand Down
13 changes: 13 additions & 0 deletions packages/salesforcedx-utils-vscode/src/helpers/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,19 @@ export const stripAnsi = (str: string): string => {
return str ? str.replaceAll(ansiRegex(), '') : str;
};

export const getMessageFromError = (err: any): string => {
if (err instanceof Error) {
return err.message;
}
if (typeof err === 'string') {
return err;
}
if (err) {
return `Unexpected error: ${JSON.stringify(err)}`;
}
return 'Unknown error';
};

/*
Copied from https://github.com/sindresorhus/strip-ansi/blob/master/index.js
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,19 @@ import {
import { CreateUtil } from '@salesforce/templates-bundle';
import * as fs from 'fs';
import * as path from 'path';
import { format } from 'util';
import * as vscode from 'vscode';
import { OUTPUT_CHANNEL } from '../channels';
import { nls } from '../messages';
import {
ComponentName,
getComponentName,
getComponentPath,
isLwcComponent,
TEST_FOLDER
} from '../util';
import { SfCommandlet, SfWorkspaceChecker } from './util';

const RENAME_LIGHTNING_COMPONENT_EXECUTOR = 'rename_lightning_component';
const RENAME_INPUT_PLACEHOLDER = 'rename_component_input_placeholder';
const RENAME_INPUT_PROMPT = 'rename_component_input_prompt';
const RENAME_INPUT_DUP_ERROR = 'rename_component_input_dup_error';
const RENAME_INPUT_DUP_FILE_NAME_ERROR =
'rename_component_input_dup_file_name_error';
const RENAME_ERROR = 'rename_component_error';
const RENAME_WARNING = 'rename_component_warning';
const LWC = 'lwc';
const AURA = 'aura';
const TEST_FOLDER = '__tests__';
import { LwcAuraDuplicateComponentCheckerForRename } from './util';
import { isNameMatch, RENAME_ERROR, RENAME_INPUT_PLACEHOLDER, RENAME_INPUT_PROMPT, RENAME_LIGHTNING_COMPONENT_EXECUTOR, RENAME_WARNING } from './util/lwcAuraDuplicateDetectionUtils';

export class RenameLwcComponentExecutor extends LibraryCommandletExecutor<ComponentName> {
private sourceFsPath: string;
Expand Down Expand Up @@ -69,14 +65,13 @@ export const renameLightningComponent = (sourceUri: vscode.Uri): void => {
const commandlet = new SfCommandlet(
new SfWorkspaceChecker(),
new GetComponentName(sourceFsPath),
new RenameLwcComponentExecutor(sourceFsPath)
new RenameLwcComponentExecutor(sourceFsPath),
new LwcAuraDuplicateComponentCheckerForRename(sourceFsPath)
);
void commandlet.run();
}
};
export type ComponentName = {
name?: string;
};

export class GetComponentName implements ParametersGatherer<ComponentName> {
private sourceFsPath: string;
constructor(sourceFsPath: string) {
Expand Down Expand Up @@ -115,9 +110,7 @@ const renameComponent = async (
): Promise<void> => {
const componentPath = await getComponentPath(sourceFsPath);
const componentName = getComponentName(componentPath);
await checkForDuplicateName(componentPath, newName);
const items = await fs.promises.readdir(componentPath);
await checkForDuplicateInComponent(componentPath, newName, items);
for (const item of items) {
// only rename the file that has same name with component
if (isNameMatch(item, componentName, componentPath)) {
Expand Down Expand Up @@ -145,106 +138,3 @@ const renameComponent = async (
await fs.promises.rename(componentPath, newComponentPath);
void notificationService.showWarningMessage(nls.localize(RENAME_WARNING));
};

export const getLightningComponentDirectory = (
sourceFsPath: string
): string => {
const directories = sourceFsPath.split(path.sep);
const rootDir = directories.includes(LWC) ? LWC : AURA;
const lwcDirectoryIndex = directories.lastIndexOf(rootDir);
if (lwcDirectoryIndex > -1) {
directories.splice(lwcDirectoryIndex + 2);
}
return directories.join(path.sep);
};

const getComponentPath = async (sourceFsPath: string): Promise<string> => {
const stats = await fs.promises.stat(sourceFsPath);
let dirname = stats.isFile() ? path.dirname(sourceFsPath) : sourceFsPath;
dirname = getLightningComponentDirectory(dirname);
return dirname;
};

const getComponentName = (componentPath: string): string =>
path.basename(componentPath);

const checkForDuplicateName = async (
componentPath: string,
newName: string
) => {
const isNameDuplicate = await isDuplicate(componentPath, newName);
if (isNameDuplicate) {
const errorMessage = nls.localize(RENAME_INPUT_DUP_ERROR);
void notificationService.showErrorMessage(errorMessage);
throw new Error(format(errorMessage));
}
};

const isDuplicate = async (
componentPath: string,
newName: string
): Promise<boolean> => {
// A LWC component can't share the same name as a Aura component
const componentPathDirName = path.dirname(componentPath);
let lwcPath: string;
let auraPath: string;
if (isLwcComponent(componentPath)) {
lwcPath = componentPathDirName;
auraPath = path.join(path.dirname(componentPathDirName), AURA);
} else {
lwcPath = path.join(path.dirname(componentPathDirName), LWC);
auraPath = componentPathDirName;
}
const allLwcComponents = await fs.promises.readdir(lwcPath);
const allAuraComponents = await fs.promises.readdir(auraPath);
return (
allLwcComponents.includes(newName) || allAuraComponents.includes(newName)
);
};

/**
* check duplicate name under current component directory and __tests__ directory to avoid file loss
*/
const checkForDuplicateInComponent = async (
componentPath: string,
newName: string,
items: string[]
) => {
let allFiles = items;
if (items.includes(TEST_FOLDER)) {
const testFiles = await fs.promises.readdir(
path.join(componentPath, TEST_FOLDER)
);
allFiles = items.concat(testFiles);
}
const allFileNames = getOnlyFileNames(allFiles);
if (allFileNames.includes(newName)) {
const errorMessage = nls.localize(RENAME_INPUT_DUP_FILE_NAME_ERROR);
void notificationService.showErrorMessage(errorMessage);
throw new Error(format(errorMessage));
}
};

const getOnlyFileNames = (allFiles: string[]) => {
return allFiles.map(file => {
const split = file?.split('.');
return split?.length > 1 ? split[0] : '';
});
};

export const isNameMatch = (
item: string,
componentName: string,
componentPath: string
): boolean => {
const isLwc = isLwcComponent(componentPath);
const regularExp = isLwc
? new RegExp(`${componentName}\\.(html|js|js-meta.xml|css|svg|test.js)`)
: new RegExp(
`${componentName}(((Controller|Renderer|Helper)?\\.js)|(\\.(cmp|app|css|design|auradoc|svg|evt)))`
);
return Boolean(item.match(regularExp));
};

const isLwcComponent = (componentPath: string): boolean =>
path.basename(path.dirname(componentPath)) === LWC;
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import { nls } from '../../messages';
import { salesforceCoreSettings } from '../../settings';
import {
CompositeParametersGatherer,
CompositePostconditionChecker,
LwcAuraDuplicateComponentCheckerForCreate,
MetadataTypeGatherer,
SelectFileName,
SelectOutputDir,
Expand Down Expand Up @@ -72,7 +74,10 @@ export const lightningGenerateAuraComponent = (): void => {
outputDirGatherer
),
createTemplateExecutor,
new OverwriteComponentPrompt()
new CompositePostconditionChecker(
new LwcAuraDuplicateComponentCheckerForCreate(),
new OverwriteComponentPrompt()
)
);
void commandlet.run();
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ import { nls } from '../../messages';
import { salesforceCoreSettings } from '../../settings';
import {
CompositeParametersGatherer,
CompositePostconditionChecker,
MetadataTypeGatherer,
SelectFileName,
SelectOutputDir,
SfCommandlet,
SfWorkspaceChecker
} from '../util';
import { LwcAuraDuplicateComponentCheckerForCreate } from '../util/lwcAuraDuplicateComponentCheckers';
import { OverwriteComponentPrompt } from '../util/overwriteComponentPrompt';
import { SelectLwcComponentType } from '../util/parameterGatherers';
import {
Expand Down Expand Up @@ -70,7 +72,10 @@ export const lightningGenerateLwc = (): void => {
new SelectOutputDir(LWC_DIRECTORY, true)
),
createTemplateExecutor,
new OverwriteComponentPrompt()
new CompositePostconditionChecker(
new LwcAuraDuplicateComponentCheckerForCreate(),
new OverwriteComponentPrompt()
)
);
void commandlet.run();
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,4 @@ export {
PathStrategyFactory,
SourcePathStrategy
} from './sourcePathStrategies';
export * from './lwcAuraDuplicateComponentCheckers';
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright (c) 2024, salesforce.com, inc.
* All rights reserved.
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/

import { getMessageFromError, notificationService, PostconditionChecker } from '@salesforce/salesforcedx-utils-vscode';
import * as fs from 'fs';
import { nls } from '../../messages';
import { ContinueOrCancel, getComponentName, getComponentPath, isContinue, OneOrMany } from '../../util';
import { isComponentName, isDirFileNameSelection} from '../../util/types';
import {
RENAME_NOT_SUPPORTED_MESSAGE,
INPUT_NO_COMPONENT_NAME_MESSAGE,
COMPONENT_CANNOT_BE_EMPTY_MESSAGE,
CREATE_NOT_SUPPORTED_MESSAGE,
INPUT_INCORRECT_COMPONENT_PROPERTIES_MESSAGE,
INPUT_DUP_ERROR,
checkForExistingComponentInAltLocation,
checkForDuplicateInComponent,
checkForDuplicateName
} from './lwcAuraDuplicateDetectionUtils';


/*
* Checks for existing component name between LWC and Aura during rename
*/
export class LwcAuraDuplicateComponentCheckerForRename implements PostconditionChecker<OneOrMany> {
constructor(private readonly sourceFsPath: string) { }
async check(inputs: ContinueOrCancel): Promise<ContinueOrCancel> {
if (!isContinue(inputs)) {
return Promise.resolve(inputs);
}
if (Array.isArray(inputs.data)) {
return { type: 'CANCEL', msg: nls.localize(RENAME_NOT_SUPPORTED_MESSAGE) };
}
const { data } = inputs;
if (!isComponentName(data)) {
return { type: 'CANCEL', msg: nls.localize(INPUT_NO_COMPONENT_NAME_MESSAGE) };
}
const { name } = data;
if (!name) {
return { type: 'CANCEL', msg: nls.localize(COMPONENT_CANNOT_BE_EMPTY_MESSAGE) };
}

try {
const componentPath = await getComponentPath(this.sourceFsPath);
await checkForDuplicateName(componentPath, name);
const items = await fs.promises.readdir(componentPath);
await checkForDuplicateInComponent(componentPath, name, items);
return { type: 'CONTINUE', data: inputs.data };
} catch (error) {
const errorMsg = getMessageFromError(error);
void notificationService.showErrorMessage(errorMsg);
return { type: 'CANCEL', msg: errorMsg };
}
}
}

/**
* Checks for existing component name between LWC and Aura during create
*/
export class LwcAuraDuplicateComponentCheckerForCreate implements PostconditionChecker<OneOrMany> {
constructor() { }
async check(inputs: ContinueOrCancel): Promise<ContinueOrCancel> {
if (!isContinue(inputs)) {
return Promise.resolve(inputs);
}
if (Array.isArray(inputs.data)) {
return { type: 'CANCEL', msg: nls.localize(CREATE_NOT_SUPPORTED_MESSAGE) };
}

if (!isDirFileNameSelection(inputs.data)) {
return { type: 'CANCEL', msg: nls.localize(INPUT_INCORRECT_COMPONENT_PROPERTIES_MESSAGE) };
}

const componentPath = inputs.data.outputdir;
const componentName = getComponentName(inputs.data.fileName);
return checkForExistingComponentInAltLocation(componentPath, componentName)
.then(exists => {
if (exists) {
void notificationService.showErrorMessage(nls.localize(INPUT_DUP_ERROR));
return { type: 'CANCEL', msg: nls.localize(INPUT_DUP_ERROR) };
}
// No duplicates found, continue with the process
return { type: 'CONTINUE', data: inputs.data };
});
}
}
Loading

0 comments on commit b7f78db

Please sign in to comment.