Skip to content

Commit

Permalink
[Build] revamp declarations (#12319)
Browse files Browse the repository at this point in the history
* [Build] revamp declarations

* formatting

* remove console output

* formatting and declaration fix

* Some fixes
  • Loading branch information
RaananW authored Apr 1, 2022
1 parent 48e2dda commit fb792c6
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 69 deletions.
144 changes: 119 additions & 25 deletions packages/dev/buildTools/src/generateDeclaration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,22 @@ export interface IGenerateDeclarationConfig {
initDocumentation?: boolean;
}

function getModuleDeclaration(source: string, filename: string, config: IGenerateDeclarationConfig, buildType: BuildType = "umd") {
function getModuleDeclaration(
source: string,
filename: string,
config: IGenerateDeclarationConfig,
buildType: BuildType = "umd",
classesMappingArray: {
alias: string;
realClassName: string;
devPackageName?: DevPackageName;
externalName?: string;
fullPath: string;
}[]
) {
const distPosition = filename.indexOf("/dist");
const packageVariables = getPackageMappingByDevName(config.devPackageName);
const moduleName = getPublicPackageName(packageVariables[buildType], undefined, filename) + filename.substring(distPosition + 5).replace(".d.ts", "");
const moduleName = getPublicPackageName(packageVariables[buildType], filename) + filename.substring(distPosition + 5).replace(".d.ts", "");
const sourceDir = path.dirname(moduleName);
const lines = source.split("\n");
const namedExportPathsToExcludeRegExp = config.namedExportPathsToExclude !== undefined ? new RegExp(`export {.*} from ".*${config.namedExportPathsToExclude}"`) : undefined;
Expand Down Expand Up @@ -53,7 +65,7 @@ function getModuleDeclaration(source: string, filename: string, config: IGenerat
if (!externalModule) {
// SKIP known named exports that are for backwards compatibility
if (namedExportPathsToExcludeRegExp && namedExportPathsToExcludeRegExp.test(line)) {
line = line.startsWith(" ") ? " //" + line.substr(3) : "// " + line;
line = line.startsWith(" ") ? " //" + line.substring(3) : "// " + line;
}

[
Expand All @@ -74,14 +86,26 @@ function getModuleDeclaration(source: string, filename: string, config: IGenerat
const newLocation = path.join(sourceDir, match[1]).replace(/\\/g, "/");
line = line.replace(match[1], newLocation);
} else {
let found = false;
Object.keys(mapping).forEach((devPackageName) => {
if (match[1].startsWith(devPackageName)) {
line = line.replace(
match[1],
getPublicPackageName(mapping[devPackageName as DevPackageName][buildType], undefined, match[1]) + match[1].substr(devPackageName.length)
getPublicPackageName(mapping[devPackageName as DevPackageName][buildType], /*undefined, */ match[1]) +
match[1].substring(devPackageName.length)
);
found = true;
}
});
if (!found) {
// not a dev dependency
// TODO - make a list of external dependencies per package
// for now - we support react
if (match[1] !== "react") {
// check what the line imports
line = "";
}
}
}
}
line = line.replace("declare ", "");
Expand Down Expand Up @@ -122,6 +146,23 @@ function getModuleDeclaration(source: string, filename: string, config: IGenerat
}
}
}
// replaces classes definitions with namespace definitions
classesMappingArray.forEach((classMapping: { alias: string; realClassName: string; devPackageName?: DevPackageName; externalName?: string; fullPath: string }) => {
const { alias, devPackageName, externalName } = classMapping;
// TODO - make a list of dependencies that are accepted by each package
if (!devPackageName) {
if (externalName) {
if (externalName === "@fortawesome" || externalName === "react-contextmenu") {
// replace with any
const matchRegex = new RegExp(`([ <])(${alias}[^;\n ]*)([^\\w])`, "g");
processedLines = processedLines.replace(matchRegex, `$1any$3`);
return;
}
}

return;
}
});
processedLines = processedLines.replace(/export declare /g, "export ");
return `declare module "${moduleName}" {
${processedLines}
Expand All @@ -132,19 +173,22 @@ ${processedLines}
/**
*
* @param source - the source code of the file
* @param originalDevPackageName - the dev package name of the file
* @param originalSourcefilePath
* @returns an array of objects with alias, realClassName and package
*/
function getClassesMap(source: string) {
const regex = /import {(.*)} from ['"](.*)['"];/g;
function getClassesMap(source: string, originalDevPackageName: string, originalSourcefilePath: string) {
const regex = /import .*{([^}]*)} from ['"](.*)['"];/g;
let matches = regex.exec(source);
const mappingArray: {
alias: string;
realClassName: string;
devPackageName: DevPackageName;
devPackageName?: DevPackageName;
externalName?: string;
fullPath: string;
}[] = [];
while (matches !== null) {
const classes = matches[1].split(",");
const classes = matches[1].split(","); //.map((className) => className.trim());
classes.forEach((className) => {
// just a typescript thing...
if (!matches) {
Expand All @@ -156,18 +200,36 @@ function getClassesMap(source: string) {
}
const realClassName = parts[0].trim();
const alias = parts[1] ? parts[1].trim() : realClassName;
const devPackageName = matches[2]!.split("/")[0];
if (alias !== realClassName) {
console.log(alias, realClassName, devPackageName, matches[2]);
}
const firstSplit = matches[2]!.split("/")[0];
const devPackageName = firstSplit[0] === "." ? originalDevPackageName : firstSplit;
// if (alias !== realClassName) {
// console.log(
// alias,
// realClassName,
// devPackageName,
// matches[2],
// isValidDevPackageName(devPackageName),
// path.resolve(path.dirname(originalSourcefilePath), matches[2]!).replace(/\\/g, "/")
// );
// }
// only internals
if (isValidDevPackageName(devPackageName)) {
mappingArray.push({
alias,
realClassName,
devPackageName,
fullPath: matches[2]!,
fullPath: firstSplit[0] === "." ? path.resolve(path.dirname(originalSourcefilePath), matches[2]!).replace(/\\/g, "/") : matches[2]!,
});
} else {
if (!devPackageName.startsWith("babylonjs")) {
console.log(`Not a Dev Package Name: ${devPackageName}`);
mappingArray.push({
alias,
externalName: devPackageName,
realClassName,
fullPath: firstSplit[0] === "." ? path.resolve(path.dirname(originalSourcefilePath), matches[2]!).replace(/\\/g, "/") : matches[2]!,
});
}
}
});
matches = regex.exec(source);
Expand All @@ -181,7 +243,8 @@ function getPackageDeclaration(
classesMappingArray: {
alias: string;
realClassName: string;
devPackageName: DevPackageName;
devPackageName?: DevPackageName;
externalName?: string;
fullPath: string;
}[],
devPackageName: DevPackageName
Expand Down Expand Up @@ -213,6 +276,7 @@ function getPackageDeclaration(
//Exclude import statements
excludeLine = excludeLine || /^import[ (]/.test(line);
excludeLine = excludeLine || /export \{/.test(line);
excludeLine = excludeLine || /export default/.test(line);
excludeLine = excludeLine || /export \* from "/.test(line);
excludeLine = excludeLine || /^declare type (.*) import/.test(line);

Expand Down Expand Up @@ -251,13 +315,41 @@ function getPackageDeclaration(
let processedSource = lines.join("\n").replace(/^(?:[\t ]*(?:\r?\n|\r))+/gm, "") + "\n\n";

// replaces classes definitions with namespace definitions
classesMappingArray.forEach((classMapping: { alias: string; realClassName: string; devPackageName: DevPackageName; fullPath: string }) => {
const { alias, realClassName, devPackageName, fullPath } = classMapping;
const namespace = getPublicPackageName(getPackageMappingByDevName(devPackageName).namespace, undefined, fullPath);
const matchRegex = new RegExp(`([ <])(${alias})([^\\w])`, "g");
processedSource = processedSource.replace(matchRegex, `$1${namespace}.${realClassName}$3`);
classesMappingArray.forEach((classMapping: { alias: string; realClassName: string; devPackageName?: DevPackageName; externalName?: string; fullPath: string }) => {
const { alias, realClassName, devPackageName, fullPath, externalName } = classMapping;
// TODO - make a list of dependencies that are accepted by each package
if (!devPackageName) {
if (externalName) {
if (externalName === "@fortawesome" || externalName === "react-contextmenu") {
// replace with any
const matchRegex = new RegExp(`([ <])(${alias}[^;\n ]*)([^\\w])`, "g");
processedSource = processedSource.replace(matchRegex, `$1any$3`);
return;
} else if (externalName === "react") {
const matchRegex = new RegExp(`([ <])(${alias})([^\\w])`, "g");
processedSource = processedSource.replace(matchRegex, `$1React.${realClassName}$3`);
}
}

return;
}
const originalNamespace = getPublicPackageName(getPackageMappingByDevName(devPackageName).namespace);
const namespace = getPublicPackageName(getPackageMappingByDevName(devPackageName).namespace, fullPath /*, fullPath*/);
if (namespace !== defaultModuleName || originalNamespace !== namespace || alias !== realClassName) {
const matchRegex = new RegExp(`([ <])(${alias})([^\\w])`, "g");
processedSource = processedSource.replace(matchRegex, `$1${namespace}.${realClassName}$3`);
}
});

processedSource = processedSource.replace(
/ global {([^}]*)}/gm,
`
}
$1
declare module ${thisFileModuleName} {
`
);

if (defaultModuleName !== thisFileModuleName) {
return `
}
Expand Down Expand Up @@ -292,16 +384,18 @@ declare module ${defaultModuleName} {
export function generateCombinedDeclaration(declarationFiles: string[], config: IGenerateDeclarationConfig, looseDeclarations: string[] = [], buildType: BuildType = "umd") {
let declarations = "";
let moduleDeclaration = "";
for (const fileName in declarationFiles) {
const declarationFile = declarationFiles[fileName];
declarationFiles.forEach((declarationFile) => {
// The lines of the files now come as a Function inside declaration file.
const data = fs.readFileSync(declarationFile, "utf8");
moduleDeclaration += getModuleDeclaration(data, declarationFile, config, config.buildType);
const classMap = getClassesMap(data, config.devPackageName, declarationFile);
moduleDeclaration += getModuleDeclaration(data, declarationFile, config, config.buildType, classMap);
if (declarationFile.indexOf("legacy.d.ts") !== -1) {
continue;
return;
}
declarations += getPackageDeclaration(data, declarationFile, getClassesMap(data), config.devPackageName);
}
// const packageMapping = getPackageMappingByDevName(config.devPackageName);
// const thisFileModuleName = getPublicPackageName(packageMapping.namespace, declarationFile);
declarations += getPackageDeclaration(data, declarationFile, classMap, config.devPackageName);
});
const looseDeclarationsString = looseDeclarations
.map((declarationFile) => {
const data = fs.readFileSync(declarationFile, "utf8");
Expand Down
Loading

0 comments on commit fb792c6

Please sign in to comment.