mirror of
https://github.com/stefanpejcic/openpanel
synced 2025-06-26 18:28:26 +00:00
packages
This commit is contained in:
23
packages/cli/src/utils/swizzle/appendAfterImports.test.ts
Normal file
23
packages/cli/src/utils/swizzle/appendAfterImports.test.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { appendAfterImports } from "./appendAfterImports";
|
||||
|
||||
describe("appendAfterImports", () => {
|
||||
it("should append after imports", () => {
|
||||
const content = `
|
||||
import { foo } from "bar";
|
||||
import { bar } from "foo";
|
||||
import { baz } from "baz";
|
||||
`;
|
||||
|
||||
const append = `console.log("hello world");`;
|
||||
|
||||
const result = appendAfterImports(content, append);
|
||||
|
||||
expect(result).toEqual(`
|
||||
import { foo } from "bar";
|
||||
import { bar } from "foo";
|
||||
import { baz } from "baz";
|
||||
console.log("hello world");
|
||||
|
||||
`);
|
||||
});
|
||||
});
|
||||
17
packages/cli/src/utils/swizzle/appendAfterImports.ts
Normal file
17
packages/cli/src/utils/swizzle/appendAfterImports.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { getImports } from "./import";
|
||||
|
||||
export const appendAfterImports = (content: string, append: string): string => {
|
||||
const imports = getImports(content);
|
||||
const lastImport = imports[imports.length - 1];
|
||||
|
||||
const lastImportIndex = lastImport
|
||||
? content.indexOf(lastImport.statement)
|
||||
: content.length - 1;
|
||||
|
||||
return `${content.slice(
|
||||
0,
|
||||
lastImportIndex + lastImport?.statement.length,
|
||||
)}\n${append}\n${content.slice(
|
||||
lastImportIndex + lastImport?.statement.length,
|
||||
)}`;
|
||||
};
|
||||
8
packages/cli/src/utils/swizzle/codes.ts
Normal file
8
packages/cli/src/utils/swizzle/codes.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export const SWIZZLE_CODES = {
|
||||
SUCCESS: "SUCCESS",
|
||||
UNKNOWN_ERROR: "UNKNOWN_ERROR",
|
||||
SOURCE_PATH_NOT_FOUND: "SOURCE_PATH_NOT_FOUND",
|
||||
TARGET_PATH_NOT_FOUND: "TARGET_PATH_NOT_FOUND",
|
||||
SOURCE_PATH_NOT_A_FILE: "SOURCE_PATH_NOT_A_FILE",
|
||||
TARGET_ALREADY_EXISTS: "TARGET_ALREADY_EXISTS",
|
||||
};
|
||||
16
packages/cli/src/utils/swizzle/getFileContent.ts
Normal file
16
packages/cli/src/utils/swizzle/getFileContent.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { readFileSync } from "fs-extra";
|
||||
import { join } from "path";
|
||||
|
||||
export function getFileContent(
|
||||
this: undefined | { absolutePackageDir?: string },
|
||||
path: string,
|
||||
): string | undefined {
|
||||
if (!this?.absolutePackageDir) {
|
||||
return undefined;
|
||||
}
|
||||
try {
|
||||
return readFileSync(join(this.absolutePackageDir, path)).toString();
|
||||
} catch (err) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
17
packages/cli/src/utils/swizzle/getPathPrefix.ts
Normal file
17
packages/cli/src/utils/swizzle/getPathPrefix.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import type { ProjectTypes } from "@definitions/projectTypes";
|
||||
import { getProjectType } from "@utils/project";
|
||||
import { getFilesPathByProject } from "@utils/resource";
|
||||
|
||||
export const getPathPrefix = () => {
|
||||
let projectType: ProjectTypes | undefined = undefined;
|
||||
|
||||
try {
|
||||
projectType = getProjectType();
|
||||
} catch (error) {
|
||||
projectType = undefined;
|
||||
}
|
||||
|
||||
const pathPrefix = getFilesPathByProject(projectType);
|
||||
|
||||
return pathPrefix;
|
||||
};
|
||||
256
packages/cli/src/utils/swizzle/import.test.ts
Normal file
256
packages/cli/src/utils/swizzle/import.test.ts
Normal file
@@ -0,0 +1,256 @@
|
||||
import { getImports, getNameChangeInImport, reorderImports } from "./import";
|
||||
|
||||
describe("getImports", () => {
|
||||
it("should get all imports", () => {
|
||||
const content = `
|
||||
import React from "react";
|
||||
import { Button } from "antd";
|
||||
import { TextInput as AntTextInput } from "antd";
|
||||
import * as Antd from "antd";
|
||||
import { Button as AntButton, TextInput } from "antd";
|
||||
import { Button as AntButton, TextInput as AntTextInput } from "antd";
|
||||
import type { IAuthProvider } from "@refinedev/core";
|
||||
import { type BaseRecord } from "@refinedev/core";
|
||||
|
||||
`;
|
||||
|
||||
const expected = [
|
||||
{
|
||||
isType: false,
|
||||
statement: 'import React from "react";',
|
||||
importPath: "react",
|
||||
defaultImport: "React",
|
||||
},
|
||||
{
|
||||
isType: false,
|
||||
statement: 'import { Button } from "antd";',
|
||||
importPath: "antd",
|
||||
namedImports: "{ Button }",
|
||||
},
|
||||
{
|
||||
isType: false,
|
||||
statement: 'import { TextInput as AntTextInput } from "antd";',
|
||||
importPath: "antd",
|
||||
namedImports: "{ TextInput as AntTextInput }",
|
||||
},
|
||||
{
|
||||
isType: false,
|
||||
statement: 'import * as Antd from "antd";',
|
||||
importPath: "antd",
|
||||
namespaceImport: "Antd",
|
||||
},
|
||||
{
|
||||
isType: false,
|
||||
statement: 'import { Button as AntButton, TextInput } from "antd";',
|
||||
importPath: "antd",
|
||||
namedImports: "{ Button as AntButton, TextInput }",
|
||||
},
|
||||
{
|
||||
isType: false,
|
||||
statement:
|
||||
'import { Button as AntButton, TextInput as AntTextInput } from "antd";',
|
||||
importPath: "antd",
|
||||
namedImports: "{ Button as AntButton, TextInput as AntTextInput }",
|
||||
},
|
||||
{
|
||||
isType: true,
|
||||
statement: 'import type { IAuthProvider } from "@refinedev/core";',
|
||||
importPath: "@refinedev/core",
|
||||
namedImports: "{ IAuthProvider }",
|
||||
},
|
||||
{
|
||||
isType: false,
|
||||
statement: 'import { type BaseRecord } from "@refinedev/core";',
|
||||
importPath: "@refinedev/core",
|
||||
namedImports: "{ type BaseRecord }",
|
||||
},
|
||||
];
|
||||
|
||||
expect(getImports(content)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getNameChangeInImport", () => {
|
||||
it("should get all name changes", () => {
|
||||
const statement = `
|
||||
{ Button as AntButton, TextInput as AntTextInput, type ButtonProps, type TextInputProps as AntTextInputProps }
|
||||
`;
|
||||
|
||||
const expected = [
|
||||
{
|
||||
statement: " Button as AntButton,",
|
||||
fromName: "Button",
|
||||
toName: "AntButton",
|
||||
afterCharacter: ",",
|
||||
},
|
||||
{
|
||||
statement: " TextInput as AntTextInput,",
|
||||
fromName: "TextInput",
|
||||
toName: "AntTextInput",
|
||||
afterCharacter: ",",
|
||||
},
|
||||
{
|
||||
afterCharacter: undefined,
|
||||
fromName: "type TextInputProps",
|
||||
statement: " type TextInputProps as AntTextInputProps ",
|
||||
toName: "AntTextInputProps",
|
||||
},
|
||||
];
|
||||
|
||||
expect(getNameChangeInImport(statement)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe("reorderImports", () => {
|
||||
it("should reorder named imports", () => {
|
||||
const content = `
|
||||
import { Button, TextInput } from "zantd";
|
||||
import { useEffect } from "react";
|
||||
import { useList } from "@refinedev/core";
|
||||
`;
|
||||
|
||||
const expected = `
|
||||
import { useEffect } from "react";
|
||||
import { useList } from "@refinedev/core";
|
||||
import { Button, TextInput } from "zantd";
|
||||
`;
|
||||
|
||||
expect(reorderImports(content).trim()).toEqual(expected.trim());
|
||||
});
|
||||
|
||||
it("should merge the same module imports", () => {
|
||||
const content = `
|
||||
import { Button, TextInput } from "antd";
|
||||
import { useEffect } from "react";
|
||||
import { useList, useOtherList, } from "@refinedev/core";
|
||||
import { useOne, useOtherOne } from "@refinedev/core";
|
||||
`;
|
||||
|
||||
const expected = `
|
||||
import { useEffect } from "react";
|
||||
import { useList, useOtherList, useOne, useOtherOne } from "@refinedev/core";
|
||||
import { Button, TextInput } from "antd";
|
||||
`;
|
||||
|
||||
expect(reorderImports(content).trim()).toEqual(expected.trim());
|
||||
});
|
||||
|
||||
it("should merge default imports with named imports", () => {
|
||||
const content = `
|
||||
import { Button, TextInput } from "antd";
|
||||
import { useEffect } from "react";
|
||||
import React from "react";
|
||||
`;
|
||||
|
||||
const expected = `
|
||||
import React, { useEffect } from "react";
|
||||
import { Button, TextInput } from "antd";
|
||||
`;
|
||||
|
||||
expect(reorderImports(content).trim()).toEqual(expected.trim());
|
||||
});
|
||||
|
||||
it("should not merge namespace imports with named imports", () => {
|
||||
const content = `
|
||||
import { Button, TextInput } from "antd";
|
||||
import { useEffect } from "react";
|
||||
import * as Antd from "antd";
|
||||
`;
|
||||
|
||||
const expected = `
|
||||
import { useEffect } from "react";
|
||||
import * as Antd from "antd";
|
||||
import { Button, TextInput } from "antd";
|
||||
`;
|
||||
|
||||
expect(reorderImports(content).trim()).toEqual(expected.trim());
|
||||
});
|
||||
|
||||
it("should not merge namespace imports with default imports", () => {
|
||||
const content = `
|
||||
import React from "react";
|
||||
import * as ReactPackage from "react";
|
||||
`;
|
||||
|
||||
const expected = `
|
||||
import * as ReactPackage from "react";
|
||||
import React from "react";
|
||||
`;
|
||||
|
||||
expect(reorderImports(content).trim()).toEqual(expected.trim());
|
||||
});
|
||||
|
||||
it("should keep name changes in named imports", () => {
|
||||
const content = `
|
||||
import { Button, TextInput } from "antd";
|
||||
import { Layout as AntLayout } from "antd";
|
||||
`;
|
||||
|
||||
const expected = `
|
||||
import { Button, TextInput, Layout as AntLayout } from "antd";
|
||||
`;
|
||||
|
||||
expect(reorderImports(content).trim()).toEqual(expected.trim());
|
||||
});
|
||||
|
||||
it("should keep the imports with comments before", () => {
|
||||
const content = `
|
||||
import React from "react";
|
||||
// comment
|
||||
import { Button, TextInput } from "antd";
|
||||
import { Layout } from "antd";
|
||||
`;
|
||||
|
||||
const expected = `
|
||||
import React from "react";
|
||||
import { Layout } from "antd";
|
||||
|
||||
// comment
|
||||
import { Button, TextInput } from "antd";
|
||||
`;
|
||||
|
||||
expect(reorderImports(content).trim()).toEqual(expected.trim());
|
||||
});
|
||||
|
||||
it("should keep type imports and add them to the end", () => {
|
||||
const content = `
|
||||
import type { Layout } from "antd";
|
||||
import React from "react";
|
||||
import { Button, TextInput } from "antd";
|
||||
`;
|
||||
|
||||
const expected = `
|
||||
import React from "react";
|
||||
import { Button, TextInput } from "antd";
|
||||
import type { Layout } from "antd";
|
||||
`;
|
||||
|
||||
expect(reorderImports(content).trim()).toEqual(expected.trim());
|
||||
});
|
||||
|
||||
it("should keep type imports with content", () => {
|
||||
const content = `
|
||||
import type { AxiosInstance } from "axios";
|
||||
import { stringify } from "query-string";
|
||||
import type { DataProvider } from "@refinedev/core";
|
||||
import { axiosInstance, generateSort, generateFilter } from "./utils";
|
||||
|
||||
type MethodTypes = "get" | "delete" | "head" | "options";
|
||||
type MethodTypesWithBody = "post" | "put" | "patch";
|
||||
`;
|
||||
|
||||
const expected = `
|
||||
import { axiosInstance, generateSort, generateFilter } from "./utils";
|
||||
import { stringify } from "query-string";
|
||||
import type { AxiosInstance } from "axios";
|
||||
import type { DataProvider } from "@refinedev/core";
|
||||
|
||||
|
||||
|
||||
type MethodTypes = "get" | "delete" | "head" | "options";
|
||||
type MethodTypesWithBody = "post" | "put" | "patch";
|
||||
`;
|
||||
|
||||
expect(reorderImports(content).trim()).toEqual(expected.trim());
|
||||
});
|
||||
});
|
||||
266
packages/cli/src/utils/swizzle/import.ts
Normal file
266
packages/cli/src/utils/swizzle/import.ts
Normal file
@@ -0,0 +1,266 @@
|
||||
const packageRegex =
|
||||
/import(?:\s+(type))?\s*(?:([^\s\{\},]+)\s*(?:,\s*)?)?(\{[^}]+\})?\s*(?:\*\s*as\s+([^\s\{\}]+)\s*)?from\s*['"]([^'"]+)['"];?/g;
|
||||
|
||||
const nameChangeRegex = /((?:\w|\s|_)*)( as )((?:\w|\s|_)*)( |,)?/g;
|
||||
|
||||
export type ImportMatch = {
|
||||
statement: string;
|
||||
importPath: string;
|
||||
defaultImport?: string;
|
||||
namedImports?: string;
|
||||
namespaceImport?: string;
|
||||
isType?: boolean;
|
||||
};
|
||||
|
||||
export type NameChangeMatch = {
|
||||
statement: string;
|
||||
fromName: string;
|
||||
toName: string;
|
||||
afterCharacter?: string;
|
||||
};
|
||||
|
||||
export const getImports = (content: string): Array<ImportMatch> => {
|
||||
const matches = content.matchAll(packageRegex);
|
||||
|
||||
const imports: Array<ImportMatch> = [];
|
||||
|
||||
for (const match of matches) {
|
||||
const [
|
||||
statement,
|
||||
typePrefix,
|
||||
defaultImport,
|
||||
namedImports,
|
||||
namespaceImport,
|
||||
importPath,
|
||||
] = match;
|
||||
|
||||
imports.push({
|
||||
isType: typePrefix === "type",
|
||||
statement,
|
||||
importPath,
|
||||
...(defaultImport && { defaultImport }),
|
||||
...(namedImports && { namedImports }),
|
||||
...(namespaceImport && { namespaceImport }),
|
||||
});
|
||||
}
|
||||
|
||||
return imports?.filter(Boolean);
|
||||
};
|
||||
|
||||
export const getNameChangeInImport = (
|
||||
namedImportString: string,
|
||||
): Array<NameChangeMatch> => {
|
||||
const matches = namedImportString.matchAll(nameChangeRegex);
|
||||
|
||||
const nameChanges: Array<NameChangeMatch> = [];
|
||||
|
||||
for (const match of matches) {
|
||||
const [statement, fromName, _as, toName, afterCharacter] = match;
|
||||
|
||||
nameChanges.push({
|
||||
statement,
|
||||
fromName: fromName.trim(),
|
||||
toName: toName.trim(),
|
||||
afterCharacter,
|
||||
});
|
||||
}
|
||||
|
||||
return nameChanges;
|
||||
};
|
||||
|
||||
/** @internal */
|
||||
export const getContentBeforeImport = (
|
||||
content: string,
|
||||
importMatch: ImportMatch,
|
||||
): string => {
|
||||
// get the content before the import statement and between the last import statement and the current one
|
||||
const contentBeforeImport = content.substring(
|
||||
0,
|
||||
content.indexOf(importMatch.statement),
|
||||
);
|
||||
// get the last import statement
|
||||
const lastImportStatement = getImports(contentBeforeImport).pop();
|
||||
|
||||
// if there is no last import statement, return the content before the current import statement
|
||||
if (!lastImportStatement) {
|
||||
return contentBeforeImport;
|
||||
}
|
||||
|
||||
// get the content between the last import statement and the current one
|
||||
const contentBetweenImports = contentBeforeImport.substring(
|
||||
contentBeforeImport.indexOf(lastImportStatement?.statement) +
|
||||
lastImportStatement?.statement?.length,
|
||||
);
|
||||
|
||||
// return the content before the current import statement and between the last import statement and the current one
|
||||
return contentBetweenImports;
|
||||
};
|
||||
|
||||
/** @internal */
|
||||
export const isImportHasBeforeContent = (
|
||||
content: string,
|
||||
importMatch: ImportMatch,
|
||||
): boolean => {
|
||||
const contentBeforeImport = importMatch
|
||||
? getContentBeforeImport(content, importMatch)
|
||||
: "";
|
||||
|
||||
return !!contentBeforeImport.trim();
|
||||
};
|
||||
|
||||
const IMPORT_ORDER = ["react", "@refinedev/core", "@refinedev/"];
|
||||
|
||||
export const reorderImports = (content: string): string => {
|
||||
let newContent = content;
|
||||
// imports can have comments before them, we need to preserve those comments and import statements.
|
||||
// so we need to filter out the imports with comments before.
|
||||
const allImports = getImports(content);
|
||||
// remove `import type` imports
|
||||
const allModuleImports = allImports.filter(
|
||||
(importMatch) => !importMatch.isType,
|
||||
);
|
||||
const typeImports = allImports.filter((importMatch) => importMatch.isType);
|
||||
|
||||
const importsWithBeforeContent: ImportMatch[] = [];
|
||||
const importsWithoutBeforeContent: ImportMatch[] = [];
|
||||
|
||||
// // remove all type imports
|
||||
typeImports.forEach((importMatch) => {
|
||||
newContent = newContent.replace(`${importMatch.statement}\n`, "");
|
||||
});
|
||||
|
||||
allModuleImports.forEach((importMatch) => {
|
||||
if (isImportHasBeforeContent(newContent, importMatch)) {
|
||||
importsWithBeforeContent.push(importMatch);
|
||||
} else {
|
||||
importsWithoutBeforeContent.push(importMatch);
|
||||
}
|
||||
});
|
||||
|
||||
// insertion point is the first import statement, others will be replaced to empty string and added to the first import line
|
||||
const insertionPoint = newContent.indexOf(
|
||||
importsWithoutBeforeContent?.[0]?.statement,
|
||||
);
|
||||
|
||||
// remove all the imports without comments before
|
||||
importsWithoutBeforeContent.forEach((importMatch) => {
|
||||
newContent = newContent.replace(importMatch.statement, "");
|
||||
});
|
||||
|
||||
// we need to merge the imports from the same package unless one of them is a namespace import]
|
||||
const importsByPackage = importsWithoutBeforeContent.reduce(
|
||||
(acc, importMatch) => {
|
||||
const { importPath } = importMatch;
|
||||
|
||||
if (acc[importPath]) {
|
||||
acc[importPath].push(importMatch);
|
||||
} else {
|
||||
acc[importPath] = [importMatch];
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, ImportMatch[]>,
|
||||
);
|
||||
|
||||
// merge the imports from the same package
|
||||
const mergedImports = Object.entries(importsByPackage).map(
|
||||
([importPath, importMatches]) => {
|
||||
// example: A
|
||||
const defaultImport = importMatches.find(
|
||||
(importMatch) => importMatch.defaultImport,
|
||||
);
|
||||
|
||||
// example: * as A
|
||||
const namespaceImport = importMatches.find(
|
||||
(importMatch) => importMatch.namespaceImport,
|
||||
);
|
||||
|
||||
// example: { A, B }
|
||||
// example: { A as C, B }
|
||||
// content inside the curly braces should be merged
|
||||
const namedImports = importMatches
|
||||
.filter((importMatch) => importMatch.namedImports)
|
||||
.map((importMatch) => {
|
||||
// remove curly braces and trim then split by comma (can be multiline)
|
||||
const namedImports = (importMatch.namedImports ?? "")
|
||||
.replace(/{|}/g, "")
|
||||
.trim()
|
||||
.split(",")
|
||||
.map((namedImport) => namedImport.trim());
|
||||
|
||||
return namedImports.filter(Boolean).join(", ");
|
||||
})
|
||||
.join(", ");
|
||||
|
||||
let importLine = "";
|
||||
|
||||
// default import and namespace import can not be used together
|
||||
// but we can use default import and named imports together
|
||||
// so we need to merge them
|
||||
if (namespaceImport) {
|
||||
importLine += `${namespaceImport.statement}\n`;
|
||||
}
|
||||
if (defaultImport || namedImports) {
|
||||
if (defaultImport && namedImports) {
|
||||
importLine += `import ${defaultImport.defaultImport}, { ${namedImports} } from "${importMatches[0].importPath}";\n`;
|
||||
} else if (defaultImport) {
|
||||
importLine += `import ${defaultImport.defaultImport} from "${importMatches[0].importPath}";\n`;
|
||||
} else {
|
||||
importLine += `import { ${namedImports} } from "${importMatches[0].importPath}";\n`;
|
||||
}
|
||||
}
|
||||
|
||||
return [importPath, importLine] as [
|
||||
importPath: string,
|
||||
importLine: string,
|
||||
];
|
||||
},
|
||||
);
|
||||
|
||||
// sort the imports without comments before
|
||||
// sort should be done by IMPORT_ORDER and alphabetically
|
||||
// priority is exact match in IMPORT_ORDER, then includes match in IMPORT_ORDER, then alphabetically
|
||||
const sortedImports = [...mergedImports].sort(
|
||||
([aImportPath], [bImportPath]) => {
|
||||
const aImportOrderIndex = IMPORT_ORDER.findIndex((order) =>
|
||||
aImportPath.includes(order),
|
||||
);
|
||||
const bImportOrderIndex = IMPORT_ORDER.findIndex((order) =>
|
||||
bImportPath.includes(order),
|
||||
);
|
||||
|
||||
if (aImportOrderIndex === bImportOrderIndex) {
|
||||
return aImportPath.localeCompare(bImportPath);
|
||||
}
|
||||
|
||||
if (aImportOrderIndex === -1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (bImportOrderIndex === -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return aImportOrderIndex - bImportOrderIndex;
|
||||
},
|
||||
);
|
||||
|
||||
// add the sorted imports to the insertion point keep the before and after content
|
||||
// add the type imports after the sorted imports
|
||||
const joinedModuleImports = sortedImports
|
||||
.map(([, importLine]) => importLine)
|
||||
.join("");
|
||||
const joinedTypeImports = [
|
||||
...typeImports.map((importMatch) => importMatch.statement),
|
||||
"",
|
||||
].join("\n");
|
||||
|
||||
newContent =
|
||||
newContent.substring(0, insertionPoint) +
|
||||
joinedModuleImports +
|
||||
joinedTypeImports +
|
||||
newContent.substring(insertionPoint);
|
||||
|
||||
return newContent;
|
||||
};
|
||||
23
packages/cli/src/utils/swizzle/index.ts
Normal file
23
packages/cli/src/utils/swizzle/index.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import path from "path";
|
||||
import type { RefineConfig } from "@definitions";
|
||||
import { provideCliHelpers } from "./provideCliHelpers";
|
||||
|
||||
export const getRefineConfig = async (
|
||||
packagePath: string,
|
||||
isAbsolute?: boolean,
|
||||
) => {
|
||||
try {
|
||||
provideCliHelpers(packagePath, isAbsolute);
|
||||
|
||||
const config = require(
|
||||
path.join(
|
||||
isAbsolute ? packagePath : path.join(process.cwd(), packagePath),
|
||||
"refine.config.js",
|
||||
),
|
||||
) as RefineConfig;
|
||||
|
||||
return config;
|
||||
} catch (error) {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
25
packages/cli/src/utils/swizzle/parseSwizzleBlocks.test.ts
Normal file
25
packages/cli/src/utils/swizzle/parseSwizzleBlocks.test.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { parseSwizzleBlocks } from "./parseSwizzleBlocks";
|
||||
|
||||
describe("parseSwizzleBlocks", () => {
|
||||
it("should remove swizzle blocks", () => {
|
||||
const content = `
|
||||
1
|
||||
// swizzle-remove-start
|
||||
remove-this
|
||||
//swizzle-remove-end
|
||||
2
|
||||
/* swizzle-remove-start */
|
||||
remove-this-too
|
||||
/* swizzle-remove-end */
|
||||
`;
|
||||
|
||||
const result = parseSwizzleBlocks(content);
|
||||
|
||||
expect(result).not.toContain("remove-this");
|
||||
expect(result).not.toContain("remove-this-too");
|
||||
expect(result).toContain("1");
|
||||
expect(result).toContain("2");
|
||||
expect(result).not.toContain("swizzle-remove-start");
|
||||
expect(result).not.toContain("swizzle-remove-end");
|
||||
});
|
||||
});
|
||||
6
packages/cli/src/utils/swizzle/parseSwizzleBlocks.ts
Normal file
6
packages/cli/src/utils/swizzle/parseSwizzleBlocks.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export const parseSwizzleBlocks = (content: string) => {
|
||||
const regex =
|
||||
/(\/\/|\/\*)(\s?)swizzle-remove-start([\s\S]*?)(\/\/|\/\*)(\s?)swizzle-remove-end(\s*)(\*\/)?/g;
|
||||
|
||||
return content.replace(regex, "");
|
||||
};
|
||||
14
packages/cli/src/utils/swizzle/prettierFormat.ts
Normal file
14
packages/cli/src/utils/swizzle/prettierFormat.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { format, resolveConfig } from "prettier";
|
||||
|
||||
export const prettierFormat = async (code: string) => {
|
||||
try {
|
||||
const prettierConfig = await resolveConfig(process.cwd());
|
||||
|
||||
return format(code, {
|
||||
...(prettierConfig ?? {}),
|
||||
parser: "typescript",
|
||||
});
|
||||
} catch (err) {
|
||||
return code;
|
||||
}
|
||||
};
|
||||
32
packages/cli/src/utils/swizzle/provideCliHelpers.ts
Normal file
32
packages/cli/src/utils/swizzle/provideCliHelpers.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import path from "path";
|
||||
import * as RefineCLI from "../../index";
|
||||
import { getFileContent } from "./getFileContent";
|
||||
|
||||
const Module = require("module");
|
||||
const originalRequire = Module.prototype.require;
|
||||
|
||||
export const provideCliHelpers = (
|
||||
packagePath: string,
|
||||
isAbsolute?: boolean,
|
||||
) => {
|
||||
Module.prototype.require = function (...args: Parameters<NodeRequire>) {
|
||||
if ((args[0] as unknown as string) === "@refinedev/cli") {
|
||||
return {
|
||||
...RefineCLI,
|
||||
getFileContent: (filePath: string) => {
|
||||
return getFileContent.call(
|
||||
{
|
||||
absolutePackageDir: isAbsolute
|
||||
? packagePath
|
||||
: path.join(process.cwd(), packagePath),
|
||||
},
|
||||
filePath,
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
//do your thing here
|
||||
return originalRequire.apply(this, args);
|
||||
};
|
||||
};
|
||||
46
packages/cli/src/utils/swizzle/renderCodeMarkdown.ts
Normal file
46
packages/cli/src/utils/swizzle/renderCodeMarkdown.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import chalk from "chalk";
|
||||
import cardinal from "cardinal";
|
||||
import boxen from "boxen";
|
||||
|
||||
const getCodeData = (content: string): { title?: string; code: string } => {
|
||||
const titleRegexp = /^(?:\/\/\s?title:\s?)(.*?)\n/g;
|
||||
|
||||
const [commentLine, titleMatch] = titleRegexp.exec(content) ?? [];
|
||||
|
||||
if (titleMatch) {
|
||||
const title = titleMatch.trim();
|
||||
const code = content.replace(commentLine || "", "");
|
||||
|
||||
return { title, code };
|
||||
}
|
||||
|
||||
return { code: content };
|
||||
};
|
||||
|
||||
export const renderCodeMarkdown = (content: string) => {
|
||||
const { title, code: rawCode } = getCodeData(content);
|
||||
|
||||
let highlighted = "";
|
||||
|
||||
// run cardinal on codeContent
|
||||
try {
|
||||
const code = cardinal.highlight(rawCode, {
|
||||
jsx: true,
|
||||
});
|
||||
highlighted = code;
|
||||
} catch (err) {
|
||||
highlighted = rawCode;
|
||||
}
|
||||
|
||||
// wrap to boxen
|
||||
const boxed = boxen(highlighted, {
|
||||
padding: 1,
|
||||
margin: 0,
|
||||
borderStyle: "round",
|
||||
borderColor: "gray",
|
||||
titleAlignment: "left",
|
||||
title: title ? chalk.bold(title) : undefined,
|
||||
});
|
||||
|
||||
return boxed;
|
||||
};
|
||||
Reference in New Issue
Block a user