Skip to content

Commit 9ca9e5e

Browse files
author
Aleksey Okhrimenko
committed
Fixig path, ref variables, hyphen and style ext
1 parent 056e97f commit 9ca9e5e

7 files changed

+84
-44
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Change Log
22

3+
## 0.1.1
4+
5+
- Fix: Get style extension from parent component (to remove schematics complexity)
6+
- Fix: reference (for example `#ref`) extraction is now not happening for local declarations
7+
- Fix: Hyphen is now allowed in component filename
8+
39
## 0.1.0
410

511
- Get style file extension from `@schematics/angular:component`

package-lock.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "arrr",
33
"displayName": "arrr",
44
"description": "The extension provides refactoring tools for your Angular codebase",
5-
"version": "0.1.0",
5+
"version": "0.1.1",
66
"publisher": "obenjiro",
77
"engines": {
88
"vscode": "^1.47.0"

src/file-picker.ts

+5-15
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,9 @@
11
import {
22
convertRelativeToFullPath,
3-
showInputBox,
4-
workspaceRoot,
3+
showInputBox
54
} from './editor';
6-
import {createFileIfDoesntExist} from './file-system';
75
import * as vscode from 'vscode';
86

9-
function completeToFullFilePath(file, folder) {
10-
if (file === NEW_FILE_OPTION) {
11-
return promptFileNameInput(folder).then(createFileIfDoesntExist as any);
12-
} else {
13-
const root = workspaceRoot();
14-
return `${root || ''}${folder}/${file}`;
15-
}
16-
}
17-
187
export function promptFileNameInput(directory) {
198
return showInputBox(directory, 'Filename or relative path to a file').then(
209
convertRelativeToFullPath as any
@@ -23,12 +12,13 @@ export function promptFileNameInput(directory) {
2312

2413
const NEW_FILE_OPTION: string = 'Enter Folder Name';
2514

26-
export function showFilePicker(directory) {
15+
export function showFilePicker() {
2716
return (
2817
vscode.window
29-
.showInputBox()
18+
.showInputBox({
19+
placeHolder: NEW_FILE_OPTION
20+
})
3021
.then(cancelActionIfNeeded)
31-
.then((file) => completeToFullFilePath(file, directory))
3222
);
3323
}
3424

src/modules/extract-to-folder-template.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@ describe('${pascalCase(componentName)}Component', () => {
3232
});`;
3333
}
3434

35-
export function getComponentText(componentName: string, targets: string[]): string {
35+
export function getComponentText(componentName: string, targets: string[], sourceComponentConfig: any): string {
3636
return `import { Component, Input } from '@angular/core';
3737
3838
@Component({
3939
selector: 'app-${componentName}',
4040
templateUrl: './${componentName}.component.html',
41-
styleUrls: ['./${componentName}.component.css']
41+
styleUrls: ['./${componentName}.component.${sourceComponentConfig.styleExt}']
4242
})
4343
export class ${pascalCase(componentName)}Component {
4444
${targets.map((target) => `@Input() ${target}`).join('\n ')}

src/modules/extract-to-folder.ts

+68-24
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
getSelectedText,
77
getSelectionOffsetRange,
88
importMissingDependencies,
9+
workspaceRoot,
910
} from "../editor";
1011
import { getAllTargets } from "../template-parser";
1112
import { showFilePicker } from "../file-picker";
@@ -32,26 +33,23 @@ export async function extractToFolder() {
3233
if (start && end) {
3334
try {
3435
const text = getSelectedText() || "";
35-
const sourceComponentName = await getComponentNameFromHtmlFile(
36+
const componentText = await getComponentTextFromHtmlFileName(
3637
activeFileName()
3738
);
38-
3939
const targets = getAllTargets(text);
40+
const sourceComponentConfig = await getCurrentComponentConfig(componentText);
4041

4142
try {
43+
const rootPath = workspaceRoot();
4244
const folderPath = await showDirectoryPicker();
43-
const filePath = (await showFilePicker(folderPath)) as string;
44-
45-
const parts = filePath.split("/");
46-
47-
const componentName = parts[parts.length - 1];
45+
const fileName = (await showFilePicker()) as string;
4846

49-
const styleExt = await getStyleExt();
47+
const fullPath = path.join(rootPath || '', folderPath, fileName);
5048

51-
const htmlFilePath = `${filePath}/${componentName}.component.html`;
52-
const cssFilePath = `${filePath}/${componentName}.component.${styleExt}`;
53-
const tsFilePath = `${filePath}/${componentName}.component.ts`;
54-
const specFilePath = `${filePath}/${componentName}.component.spec.ts`;
49+
const htmlFilePath = `${fullPath}/${fileName}.component.html`;
50+
const cssFilePath = `${fullPath}/${fileName}.component.${sourceComponentConfig.styleExt}`;
51+
const tsFilePath = `${fullPath}/${fileName}.component.ts`;
52+
const specFilePath = `${fullPath}/${fileName}.component.spec.ts`;
5553

5654
await createFileIfDoesntExist(htmlFilePath);
5755
await createFileIfDoesntExist(cssFilePath);
@@ -61,15 +59,15 @@ export async function extractToFolder() {
6159
await appendSelectedTextToFile({ text }, htmlFilePath);
6260
await appendSelectedTextToFile({ text: `` }, cssFilePath);
6361
await appendSelectedTextToFile(
64-
{ text: getComponentText(componentName, targets) },
62+
{ text: getComponentText(fileName, targets, sourceComponentConfig) },
6563
tsFilePath
6664
);
6765
await appendSelectedTextToFile(
68-
{ text: getSpecText(componentName) },
66+
{ text: getSpecText(fileName) },
6967
specFilePath
7068
);
7169

72-
const componentInstance = getComponentInstance(componentName, targets);
70+
const componentInstance = getComponentInstance(fileName, targets);
7371
await persistFileSystemChanges(replaceSelectionWith(componentInstance));
7472

7573
const moduleUris = await vscode.workspace.findFiles(
@@ -83,7 +81,7 @@ export async function extractToFolder() {
8381
const targetModuleDocuments = moduleDocuments.filter(
8482
(moduleDocument) => {
8583
const allText = moduleDocument.getText();
86-
return new RegExp(`\\b${sourceComponentName}\\b`).test(allText);
84+
return new RegExp(`\\b${sourceComponentConfig.componentName}\\b`).test(allText);
8785
}
8886
);
8987

@@ -99,7 +97,7 @@ export async function extractToFolder() {
9997
const start = moduleDocument.positionAt(startOffset);
10098
const end = moduleDocument.positionAt(endOffset);
10199
const targetText = `${matches[0]}\n ${pascalCase(
102-
componentName
100+
fileName
103101
)}Component,`;
104102

105103
return replaceTextInFile(
@@ -126,23 +124,69 @@ export async function extractToFolder() {
126124
}
127125
}
128126

129-
async function getComponentNameFromHtmlFile(filePath) {
127+
async function getComponentTextFromHtmlFileName(filePath): Promise<string> {
130128
const name = path.basename(filePath);
131129
const dir = path.dirname(filePath);
132130

133131
const tsPath = path.join(dir, name.replace(".html", ".ts"));
134132
const tsContent = fs.readFileSync(tsPath, "utf-8");
135133

136-
return (tsContent.match(/export class\s+([\w_]+)/) || [])[1];
134+
return tsContent;
137135
}
138136

139-
async function getStyleExt() {
137+
async function getCurrentComponentConfig(componentText) {
140138
try {
141-
const [angularJsonPath] = await vscode.workspace.findFiles("angular.json");
142-
const config = JSON.parse(fs.readFileSync(angularJsonPath.path, "utf-8"));
139+
const ts = require('typescript');
140+
const node = ts.createSourceFile(
141+
'x.ts',
142+
componentText,
143+
ts.ScriptTarget.Latest // langugeVersion
144+
);
145+
146+
let classDecl;
147+
node.forEachChild(child => {
148+
if (
149+
ts.SyntaxKind[child.kind] === 'ClassDeclaration' &&
150+
child.decorators[0].expression.expression.escapedText === 'Component'
151+
) {
152+
classDecl = child;
153+
}
154+
});
155+
// const decoratorName = classDecl.decorators[0].expression.expression.escapedText;
156+
const decoratorParams =
157+
classDecl.decorators[0].expression.arguments.reduce((acc, el) => {
158+
el.properties.forEach(
159+
prop => acc[prop.name.escapedText] = prop.initializer.elements ? prop.initializer.elements.map(e => e.text) : prop.initializer.text
160+
);
161+
return acc;
162+
}, {});
163+
164+
const styleInline = Boolean(decoratorParams.style);
165+
166+
return {
167+
componentName: classDecl.name.escapedText,
168+
styleInline,
169+
styleExt: styleInline ? 'css': trimChar(path.extname(decoratorParams.styleUrls[0] || 'fail.css'), '.')
170+
};
143171

144-
return config.schematics["@schematics/angular:component"].styleext;
145172
} catch (e) {
146-
return "css";
173+
174+
return {
175+
componentName: (componentText.match(/export class\s+([\w_]+)/) || [])[1],
176+
styleInline: false,
177+
styleExt: 'css'
178+
};
179+
147180
}
148181
}
182+
183+
function escapeRegExp(strToEscape) {
184+
// Escape special characters for use in a regular expression
185+
return strToEscape.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
186+
};
187+
188+
function trimChar(origString, charToTrim) {
189+
charToTrim = escapeRegExp(charToTrim);
190+
var regEx = new RegExp("^[" + charToTrim + "]+|[" + charToTrim + "]+$", "g");
191+
return origString.replace(regEx, "");
192+
};

src/template-parser.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ export function getAllTargets(text) {
160160
visitTarget(
161161
ast,
162162
(value: any) => {
163-
return getNodeCtor(value) === "Variable";
163+
return getNodeCtor(value) === "Variable" || getNodeCtor(value) === "Reference";
164164
},
165165
(node: any, parent: any) => {
166166
if (targets.indexOf(node.name) > -1) {

0 commit comments

Comments
 (0)