lucy-cli
Version:
Lucy Framework for WIX Studio Editor
289 lines (288 loc) • 15.8 kB
JavaScript
import gulp from 'gulp';
import * as path from 'path';
import replace from 'gulp-string-replace';
import flatmap from 'gulp-flatmap';
import jeditor from 'gulp-json-editor';
import merge from 'merge-stream';
import * as insert from 'gulp-insert';
import { blue, orange, red, yellow } from '../index.js';
import tap from 'gulp-tap';
export function updateWixTypes(options) {
return () => {
const { publicSettings, backendSettings, masterSettings, pageSettings, replaceOptions } = options;
let count = 0;
const modules = Object.assign({}, options.projectSettings?.modules, options.projectSettings?.lucySettings?.modules);
let localModules = undefined;
if (options.projectSettings?.lucySettings) {
localModules = options.projectSettings.lucySettings.modules;
}
// Add module to publicSettings
publicSettings.compilerOptions.paths['backend/*.web'] = ["../../../typescript/backend/*.web.ts"];
publicSettings.compilerOptions.paths['backend/*.web.js'] = ["../../../typescript/backend/*.web.ts"];
publicSettings.compilerOptions.paths['backend/*'] = ["../../../typescript/backend/*.web.ts"];
publicSettings.compilerOptions.paths['backend/*.jsw'] = ["../../../typescript/backend/*.jsw.ts"];
publicSettings.compilerOptions.paths.mocks = ["../../../typescript/__mocks__/*"];
publicSettings.compilerOptions.paths['types/*'] = ["../../../typescript/types/*"];
publicSettings.compilerOptions.paths['public/*'] = ["../../../typescript/public/*.ts"];
publicSettings.include = [
"../../../typescript/public/**/*",
"../../../typescript/__mocks__/**/*",
// "../../../typescript/backend/**/*.jsw.ts",
// "../../../typescript/backend/**/*.web.ts",
// "../../../typescript/backend/**/*.web"
];
// Add module to backendSettings
backendSettings.compilerOptions.paths.mocks = ["../../../typescript/__mocks__/*"];
backendSettings.compilerOptions.paths['types/*'] = [`../../../typescript/types/*`];
backendSettings.include = [
"../../../typescript/backend/**/*",
"../../../typescript/__mocks__/**/*",
// "../../../typescript/backend/**/*.jsw.ts",
// "../../../typescript/backend/**/*.web.ts",
// "../../../typescript/backend/**/*.web"
];
// Add module to masterSettings
masterSettings.compilerOptions.paths['backend/*.web'] = ["../../../typescript/backend/*.web.ts"];
masterSettings.compilerOptions.paths['backend/*.web.js'] = ["../../../typescript/backend/*.web.ts"];
masterSettings.compilerOptions.paths['backend/*'] = ["../../../typescript/backend/*.web.ts"];
masterSettings.compilerOptions.paths['backend/*.jsw'] = ["../../../typescript/backend/*.jsw.ts"];
masterSettings.compilerOptions.paths['types/*'] = ["../../../typescript/types/*"];
masterSettings.include = [
"../../../typescript/public/**/*",
"../../../typescript/__mocks__/**/*",
// "../../../typescript/backend/**/*.jsw.ts",
// "../../../typescript/backend/**/*.web.ts",
// "../../../typescript/backend/**/*.web"
];
// Add module to pageSettings
pageSettings.compilerOptions.paths['backend/*.web'] = ["../../../typescript/backend/*.web.ts"];
pageSettings.compilerOptions.paths['backend/*.web.js'] = ["../../../typescript/backend/*.web.ts"];
pageSettings.compilerOptions.paths['backend/*'] = ["../../../typescript/backend/*.web.ts"];
pageSettings.compilerOptions.paths['backend/*.jsw'] = ["../../../typescript/backend/*.jsw.ts"];
pageSettings.compilerOptions.paths['types/*'] = ["../../../typescript/types/*"];
pageSettings.compilerOptions.paths['backend/*.jsw'] = ["../../../typescript/backend/*.jsw.ts"];
pageSettings.include = [
"../../../typescript/public/**/*",
"../../../typescript/__mocks__/**/*",
// "../../../typescript/backend/**/*.jsw.ts",
// "../../../typescript/backend/**/*.web.ts",
// "../../../typescript/backend/**/*.web"
];
if (modules) {
for (const [name] of Object.entries(modules)) {
// Add module to publicSettings
publicSettings.compilerOptions.paths['backend/*.web.js'].push(`../../../${name}/backend/*.web.ts`);
publicSettings.compilerOptions.paths['backend/*.web'].push(`../../../${name}/backend/*.web.ts`);
publicSettings.compilerOptions.paths['backend/*'].push(`../../../${name}/backend/*.web.ts`);
publicSettings.compilerOptions.paths['backend/*.jsw'].push(`../../../${name}/backend/*.jsw.ts`);
publicSettings.compilerOptions.paths['public/*'].push(`../../../${name}/public/*`);
publicSettings.compilerOptions.paths.mocks.push(...[`../../../${name}/__mocks__/*`]);
publicSettings.compilerOptions.paths['types/*'].push(`../../../${name}/types/*`);
publicSettings.include.push(...[
`../../../${name}/public/**/*`,
`../../../${name}__mocks__/**/*`,
// `../../../${name}/backend/**/*.jsw.ts`,
// `../../../${name}/backend/**/*.web.ts`,
// `../../../${name}/backend/**/*.web`
]);
// Add module to backendSettings
backendSettings.compilerOptions.paths['public/*'].push(`../../../${name}/public/*`);
backendSettings.compilerOptions.paths['backend/*'].push(`../../../${name}/backend/*`);
backendSettings.compilerOptions.paths.mocks.push(...[`../../../${name}/__mocks__/*`]);
backendSettings.compilerOptions.paths['types/*'].push(`../../../${name}/types/*`);
backendSettings.include.push(...[
`../../../${name}/public/**/*`,
`../../../${name}__mocks__/**/*`,
`../../../${name}/backend/**/*`,
// `../../../${name}/backend/**/*.jsw.ts`,
// `../../../${name}/backend/**/*.web.ts`,
// `../../../${name}/backend/**/*.web`
]);
// Add module to masterSettings
masterSettings.compilerOptions.paths['backend/*.web.js'].push(`../../../${name}/backend/*.web.ts`);
masterSettings.compilerOptions.paths['backend/*.web'].push(`../../../${name}/backend/*.web.ts`);
masterSettings.compilerOptions.paths['backend/*'].push(`../../../${name}/backend/*.web.ts`);
masterSettings.compilerOptions.paths['backend/*.jsw'].push(`../../../${name}/backend/*.jsw.ts`);
masterSettings.compilerOptions.paths['public/*'].push(`../../../${name}/public/*`);
masterSettings.compilerOptions.paths['types/*'].push(`../../../${name}/types/*`);
masterSettings.include.push(...[
`../../../${name}/public/**/*`,
`../../../${name}__mocks__/**/*`,
// `../../../${name}/backend/**/*.jsw.ts`,
// `../../../${name}/backend/**/*.web.ts`,
// `../../../${name}/backend/**/*.web`
]);
// Add module to pageSettings
pageSettings.compilerOptions.paths['backend/*.web.js'].push(`../../../${name}/backend/*.web.ts`);
pageSettings.compilerOptions.paths['backend/*.web'].push(`../../../${name}/backend/*.web.ts`);
pageSettings.compilerOptions.paths['backend/*'].push(`../../../${name}/backend/*.web.ts`);
pageSettings.compilerOptions.paths['backend/*.jsw'].push(`../../../${name}/backend/*.jsw.ts`);
pageSettings.compilerOptions.paths['public/*'].push(`../../../${name}/public/*`);
pageSettings.compilerOptions.paths['types/*'].push(`../../../${name}/types/*`);
pageSettings.include.push(...[
`../../../${name}/public/**/*`,
`../../../${name}__mocks__/**/*`,
// `../../../${name}/backend/**/*.jsw.ts`,
// `../../../${name}/backend/**/*.web.ts`,
// `../../../${name}/backend/**/*.web`
]);
}
}
return gulp.src(['./.wix/types/**/*.json', '!./.wix/types/wix-code-types/**/*'])
.pipe(flatmap(function (stream, file) {
count++;
if (file.dirname.endsWith('public')) {
return stream.pipe(jeditor(publicSettings))
.pipe(jeditor((json) => cleanTsConfig(json)))
.pipe(jeditor((json) => processJson(json)))
.pipe(replace('"../backend.d.ts",', '', replaceOptions))
.pipe(replace('"../../../src/backend/\\*\\*/\\*.web.js",', '', replaceOptions));
}
if (file.dirname.endsWith('backend')) {
return stream.pipe(jeditor(backendSettings))
.pipe(jeditor((json) => cleanTsConfig(json)))
.pipe(jeditor((json) => processJson(json)));
}
;
if (file.dirname.endsWith('masterPage')) {
return stream.pipe(jeditor(masterSettings))
.pipe(jeditor((json) => cleanTsConfig(json)))
.pipe(jeditor((json) => processJson(json)))
.pipe(replace('"../backend.d.ts",', '', replaceOptions))
.pipe(replace('"../../../src/backend/\\*\\*/\\*.web.js",', '', replaceOptions));
}
;
return stream.pipe(jeditor(pageSettings))
.pipe(jeditor((json) => cleanTsConfig(json)))
.pipe(jeditor((json) => processJson(json)))
.pipe(replace('"../backend.d.ts",', '', replaceOptions))
.pipe(replace('"../../../src/backend/\\*\\*/\\*.web.js",', '', replaceOptions));
}))
.pipe(replace('masterPage.masterPage.js', 'masterPage.ts', replaceOptions))
.pipe(replace('/src/', '/typescript/', replaceOptions))
.pipe(replace('.js"', '.ts"', replaceOptions))
.pipe(tap(function (file) {
// console.debug(file.contents?.toString() ?? 'no content'); TODO: Uncomment when needed
}))
.pipe(gulp.dest((file) => {
const filePath = file.dirname;
const outputDir = path.dirname(filePath);
return path.join(outputDir);
}))
.on('error', function (e) {
console.log("💩" + red.underline.bold('Modification of WIX configs failed!'));
console.log("💩" + red.underline.bold(` => Error: ${orange(e.message)}`));
this.emit('end');
})
.on('end', function () { console.log("🐶" + blue.underline(`Modification of ${yellow(count)} WIX configs succeeded!`)); });
};
}
export function addTypes(options, done) {
const { replaceOptions } = options;
// Regex to match keywords at the beginning of a line, capturing any leading whitespace.
// The 'm' flag is for multiline matching (^ matches start of line).
// The 'g' flag is for global matching (replace all instances).
const interfaceRegex = /^(\s*)interface\s/gm;
const enumRegex = /^(\s*)enum\s/gm;
const typeRegex = /^(\s*)type\s/gm;
const exportTypes = gulp.src(['./.wix/types/wix-code-types/dist/types/common/*.d.ts', '!./.wix/types/wix-code-types/dist/types/common/$w.d.ts'])
// The replacement string '$1export...' uses the captured whitespace to preserve indentation.
.pipe(replace(interfaceRegex, 'export interface ', replaceOptions))
.pipe(replace(enumRegex, 'export enum ', replaceOptions))
.pipe(replace(typeRegex, 'export type ', replaceOptions))
.pipe(gulp.dest('./.wix/types/wix-code-types/dist/types/common/'));
const exportTypesBeta = gulp.src(['./.wix/types/wix-code-types/dist/types/beta/common/*.d.ts', '!./.wix/types/wix-code-types/dist/types/beta/common/$w.d.ts'])
.pipe(replace(interfaceRegex, '$1export interface ', replaceOptions))
.pipe(replace(enumRegex, 'export enum ', replaceOptions))
.pipe(replace(typeRegex, 'export type ', replaceOptions))
.pipe(gulp.dest('./.wix/types/wix-code-types/dist/types/beta/common/'));
const processCommon = gulp.src(['./.wix/types/wix-code-types/dist/types/common/$w.d.ts'])
.pipe(insert.prepend("import '@total-typescript/ts-reset';\n"))
.pipe(gulp.dest('./.wix/types/wix-code-types/dist/types/common/'));
return merge(processCommon, exportTypesBeta, exportTypes)
.on('error', function (e) {
console.log("💩" + red.underline.bold(' => Updating WIX failed!'));
console.log("💩" + red.underline.bold(` => Error: ${orange(e.message)}`));
this.emit('end');
done();
})
.on('end', function () {
console.log("🐶" + blue.underline(' => Updating WIX succeeded!'));
done();
});
}
;
function makeHashable(obj) {
if (Array.isArray(obj)) {
return obj.map(makeHashable);
}
else if (obj && typeof obj === 'object') {
const sortedKeys = Object.keys(obj).sort();
const result = {};
for (const key of sortedKeys) {
result[key] = makeHashable(obj[key]);
}
return result;
}
else {
return obj;
}
}
function removeDuplicatesFromArray(arr) {
const seen = new Set();
const newArr = [];
for (const item of arr) {
const processedItem = processJson(item); // Recursively process the item
const hashableItem = makeHashable(processedItem);
const serializedItem = JSON.stringify(hashableItem);
if (!seen.has(serializedItem)) {
seen.add(serializedItem);
newArr.push(processedItem);
}
}
return newArr;
}
function processJson(obj) {
if (Array.isArray(obj)) {
// Process arrays and remove duplicate items
const uniqueArray = removeDuplicatesFromArray(obj);
return uniqueArray.length > 0 ? uniqueArray : undefined; // Remove empty arrays
}
else if (obj && typeof obj === 'object') {
// Process objects, ensuring keys are unique
const result = {};
const seenKeys = new Set();
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
if (!seenKeys.has(key)) {
const processedValue = processJson(obj[key]);
if (processedValue !== undefined) { // Remove keys with undefined values
seenKeys.add(key);
result[key] = processedValue;
}
}
}
}
// If all keys were empty or undefined, return undefined
return Object.keys(result).length > 0 ? result : undefined;
}
else {
// Return primitive values as-is
return obj;
}
}
function cleanTsConfig(json) {
// const paths: Record<string, string[]> = {};
if (json.compilerOptions?.paths) {
for (const [key, value] of Object.entries(json.compilerOptions.paths)) {
const uniquePaths = new Set(value);
json.compilerOptions.paths[key] = Array.from(uniquePaths);
// paths[key] = Array.from(uniquePaths);
}
// json.compilerOptions.paths = paths;
}
json.include = Array.from(new Set(json.include));
json.exclude = Array.from(new Set(json.exclude));
json.files = Array.from(new Set(json.files));
return json;
}