lincd-cli
Version:
Command line tools for the lincd.js library
1,095 lines (1,093 loc) ⢠116 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.removeOldFiles = exports.executeCommandForPackage = exports.addCapacitor = exports.addLinesToFile = exports.executeCommandForEachPackage = exports.buildUpdated = exports.publishPackage = exports.publishUpdated = exports.compilePackageCJS = exports.compilePackageESM = exports.compilePackage = exports.buildPackage = exports.register = exports.createPackage = exports.upgradePackages = exports.buildBackend = exports.buildFrontend = exports.buildApp = exports.startServer = exports.runMethod = exports.runScript = exports.ensureEnvironmentLoaded = exports.depCheck = exports.depCheckStaged = exports.checkImports = exports.createComponent = exports.createSetComponent = exports.createShape = exports.getScriptDir = exports.setNameVariables = exports.createOntology = exports.getLincdPackages = exports.buildAll = exports.runOnPackagesGroupedByDependencies = exports.developPackage = exports.logError = exports.warn = exports.createApp = void 0;
const chalk_1 = __importDefault(require("chalk"));
const child_process_1 = require("child_process");
const depcheck_1 = __importDefault(require("depcheck"));
const get_env_vars_js_1 = require("env-cmd/dist/get-env-vars.js");
const fs_extra_1 = __importDefault(require("fs-extra"));
const path_1 = __importStar(require("path"));
const readline_1 = require("readline");
const utils_js_1 = require("./utils.js");
const child_process_2 = require("child_process");
const find_nearest_package_json_1 = require("find-nearest-package-json");
const fs_1 = require("fs");
const LinkedFileStorage_1 = require("lincd/utils/LinkedFileStorage");
// import pkg from 'lincd/utils/LinkedFileStorage';
// const { LinkedFileStorage } = pkg;
// const config = require('lincd-server/site.webpack.config');
const glob_1 = require("glob");
const webpack_1 = __importDefault(require("webpack"));
const ora_1 = __importDefault(require("ora"));
const staged_git_files_1 = __importDefault(require("staged-git-files"));
let dirname__ = typeof __dirname !== 'undefined'
? __dirname
: //@ts-ignore
(0, path_1.dirname)(import.meta.url).replace('file:/', '');
var variables = {};
/**
* Prompt user for input
*/
function promptUser(question) {
const rl = (0, readline_1.createInterface)({
input: process.stdin,
output: process.stdout,
});
return new Promise((resolve) => {
rl.question(chalk_1.default.cyan(question), (answer) => {
rl.close();
resolve(answer.trim());
});
});
}
const createApp = async (name, basePath = process.cwd()) => {
// If no name provided, prompt for folder name first
if (!name) {
console.log(chalk_1.default.blue('\nš Folder name for your app:\n'));
const folderNameInput = await promptUser('Folder name (e.g., "my-app"): ');
if (!folderNameInput || !folderNameInput.trim()) {
console.warn(chalk_1.default.red('Folder name is required. Aborting.'));
return;
}
name = folderNameInput.trim();
}
let { hyphenName, camelCaseName, underscoreName } = (0, exports.setNameVariables)(name);
// Prompt user for app configuration
console.log(chalk_1.default.blue('\nš Please provide the following information:\n'));
console.log(chalk_1.default.gray('(Press Enter to use defaults based on folder name)\n'));
const defaultAppName = name
.split(/[-_\s]+/)
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
.join(' ');
const defaultAppPrefix = underscoreName;
const defaultAppDomain = hyphenName + '.com';
const appNameInput = await promptUser(`App Name (display name) [${chalk_1.default.gray(defaultAppName)}]: `);
const appPrefixInput = await promptUser(`App Prefix (short code for data files, e.g., "myapp") [${chalk_1.default.gray(defaultAppPrefix)}]: `);
const appDomainInput = await promptUser(`App Domain [${chalk_1.default.gray(defaultAppDomain)}]: `);
const appName = appNameInput || defaultAppName;
const appPrefix = appPrefixInput || defaultAppPrefix;
const appDomain = appDomainInput || defaultAppDomain;
// Set new variables for app configuration
setVariable('app_name', appName);
setVariable('app_prefix', appPrefix);
setVariable('app_domain', appDomain);
let targetFolder = path_1.default.join(basePath, hyphenName);
if (!fs_extra_1.default.existsSync(targetFolder)) {
fs_extra_1.default.mkdirSync(targetFolder);
}
fs_extra_1.default.copySync(path_1.default.join(dirname__, '..', '..', 'defaults', 'app-with-backend'), targetFolder);
//make sure the data folder exists (even though its empty).. copying empty folders does not work with fs.copySync
fs_extra_1.default.mkdirSync(path_1.default.join(targetFolder, 'data'), { recursive: true });
fs_extra_1.default.mkdirSync(path_1.default.join(targetFolder, 'data/uploads/resized'), {
recursive: true,
});
fs_extra_1.default.renameSync(path_1.default.join(targetFolder, 'gitignore.template'), path_1.default.join(targetFolder, '.gitignore'));
fs_extra_1.default.renameSync(path_1.default.join(targetFolder, 'yarnrc.yml.template'), path_1.default.join(targetFolder, '.yarnrc.yml'));
// fs.copySync(path.join(__dirname, '..', 'defaults', 'app'), targetFolder);
log("Creating new LINCD application '" + appName + "'");
//replace variables in some copied files
await replaceVariablesInFolder(targetFolder);
let hasYarn = await hasYarnInstalled();
let installCommand = hasYarn
? 'export NODE_OPTIONS="--no-network-family-autoselection" && yarn install'
: 'npm install';
await (0, utils_js_1.execp)(`cd ${hyphenName} && ${installCommand}`, true).catch((err) => {
console.warn('Could not install dependencies or start application');
});
log(`Your LINCD App is ready at ${chalk_1.default.blueBright(targetFolder)}`, `To start, run\n${chalk_1.default.blueBright(`cd ${hyphenName}`)} and then ${chalk_1.default.blueBright((hasYarn ? 'yarn' : 'npm') + ' start')}`);
};
exports.createApp = createApp;
function logHelp() {
(0, utils_js_1.execp)('yarn exec lincd help');
}
function log(...messages) {
messages.forEach((message) => {
console.log(chalk_1.default.cyan('Info: ') + message);
});
}
function progressUpdate(message) {
process.stdout.write(' \r');
process.stdout.write(message + '\r');
}
function warn(...messages) {
messages.forEach((message) => {
console.warn(chalk_1.default.redBright('Warning: ') + message);
// console.log(chalk.red(message));
});
}
exports.warn = warn;
function logError(...messages) {
messages.forEach((message) => {
console.error(chalk_1.default.redBright('Error: ') + message);
});
}
exports.logError = logError;
function developPackage(target, mode) {
if (!target)
target = 'es6';
if (mode !== 'production')
mode = '';
else if (target !== 'es6')
log('target must be es6 when developing for production');
if (target == 'es5' || target == 'es6') {
// log('> Starting continuous development build for '+target+' target')
log('starting continuous development build');
log('grunt dev' +
(target ? '-' + target : '') +
(mode ? '-' + mode : '') +
' --color');
var command = (0, child_process_1.exec)('grunt dev' +
(target ? '-' + target : '') +
(mode ? '-' + mode : '') +
' --color');
command.stdout.pipe(process.stdout);
command.stderr.pipe(process.stderr);
}
else {
console.warn('unknown build target. Use es5 or es6');
}
}
exports.developPackage = developPackage;
function checkWorkspaces(rootPath, workspaces, res) {
// console.log('checking workspaces at '+rootPath+": "+workspaces.toString());
if (workspaces.packages) {
workspaces = workspaces.packages;
}
workspaces.forEach((workspace) => {
let workspacePath = path_1.default.join(rootPath, workspace.replace('/*', ''));
if (workspace.indexOf('/*') !== -1) {
// console.log(workspacePath);
if (fs_extra_1.default.existsSync(workspacePath)) {
let folders = fs_extra_1.default.readdirSync(workspacePath);
folders.forEach((folder) => {
if (folder !== './' && folder !== '../') {
checkPackagePath(rootPath, path_1.default.join(workspacePath, folder), res);
}
});
}
}
else {
checkPackagePath(rootPath, workspacePath, res);
}
});
}
function checkPackagePath(rootPath, packagePath, res) {
let packageJsonPath = path_1.default.join(packagePath, 'package.json');
// console.log('checking '+packagePath);
if (fs_extra_1.default.existsSync(packageJsonPath)) {
var pack = JSON.parse(fs_extra_1.default.readFileSync(packageJsonPath, 'utf8'));
//some packages are not true lincd packages, but we still want them to be re-built automatically. This is what lincd_util is for
if (pack && pack.workspaces) {
checkWorkspaces(packagePath, pack.workspaces, res);
}
else if (pack && pack.lincd === true) {
res.push({
path: packagePath,
packageName: pack.name,
});
}
}
}
function runOnPackagesGroupedByDependencies(lincdPackages, onBuildStack, onStackEnd, sync = false) {
let dependencies = new Map();
let res, rej;
const deferredPromise = new Promise((resolve, reject) => {
res = resolve;
rej = reject;
});
//get dependencies of each package
let leastDependentPackage;
lincdPackages.forEach((pkg) => {
var pack = (0, utils_js_1.getPackageJSON)(pkg.path);
if (pack) {
//get lincd related dependencies and get the actual package details from the package map by removing '@dacore/' from the package name
let packageDependencies = Object.keys(pack.dependencies)
.filter((dependency) => lincdPackages.has(dependency))
.map((dependency) => {
return lincdPackages.has(dependency)
? lincdPackages.get(dependency)
: dependency;
});
// console.log(package.packageName,packageDependencies.map())
dependencies.set(pkg, packageDependencies);
}
});
dependencies.forEach((PackageDependencies, pkg) => {
if (!PackageDependencies.some((dependency) => {
return (typeof dependency !== 'string' &&
lincdPackages.has(dependency.packageName));
})) {
leastDependentPackage = pkg;
}
});
let startStack = leastDependentPackage
? [leastDependentPackage]
: [];
const runPackage = async (runFunction, pck) => {
try {
const result = await runFunction(pck);
done.add(pck);
return result;
}
catch (errorObj) {
if (errorObj.error) {
let { error, stdout, stderr } = errorObj;
warn('Uncaught exception whilst running parallel function on ' +
pck.packageName, (error === null || error === void 0 ? void 0 : error.message) ? error.message : error === null || error === void 0 ? void 0 : error.toString());
}
else {
warn('Uncaught exception whilst running parallel function on ' +
pck.packageName, errorObj === null || errorObj === void 0 ? void 0 : errorObj.toString());
process.exit();
}
// warn(chalk.red(pck.packageName+' failed:'));
// console.log(stdout);
done.add(pck);
return undefined;
}
};
let done = new Set();
let results = [];
let runStack = async (stack) => {
let runFunction = onBuildStack(stack, dependencies);
let stackPromise;
if (sync) {
//build the stack in parallel
stackPromise = Promise.resolve(true);
stack.forEach((pck) => {
stackPromise = stackPromise.then(() => {
return runPackage(runFunction, pck);
});
});
}
else {
//build the stack in parallel
stackPromise = Promise.all(stack.map((pck) => {
return runPackage(runFunction, pck);
}));
}
//wait till stack is completed
let stackResults = await stackPromise;
results = results.concat(stackResults);
//clear stack for next round
stack = [];
//find those packages who have all their dependencies already built and add them to the stack
lincdPackages.forEach((pkg) => {
let deps = dependencies.get(pkg);
//if the package is not done yet
//but every dependency is now done OR was not something we can build (some @dacore dependencies may not be local)
if (!done.has(pkg) &&
deps.every((dependency) => {
return (typeof dependency !== 'string' &&
(done.has(dependency) || !lincdPackages.has(dependency.packageName)));
})) {
stack.push(pkg);
}
});
if (stack.length <= 0 && done.size < lincdPackages.size) {
console.log(chalk_1.default.red('Only ' +
done.size +
' out of ' +
lincdPackages.size +
' packages have been built'));
console.log('ALL remaining packages have dependencies that have not been met. This may point to ' +
chalk_1.default.red('circular dependencies.'));
console.log('Already built: ' +
Array.from(done)
.map((p) => chalk_1.default.green(p.packageName))
.join(', '));
console.log(chalk_1.default.blue('\nTo solve this issue') +
': find the circular dependencies below and fix the dependencies:\n\n');
//TODO: actually find and name the packages that have circular dependencies
// let circular = [];
// lincdPackages.forEach((pkg) => {
// if (!done.has(pkg))
// {
// let deps = dependencies.get(pkg);
// if (deps.some(dependency => {
// //return true if this dependency (indirectly) depends on the package whos' dependency it is
// return hasDependency(dependency,pkg,dependencies)
// }))
// {
// circular.push(pkg);
// }
// process.exit();
// }
// });
lincdPackages.forEach((pkg) => {
let deps = dependencies.get(pkg);
if (!done.has(pkg)) {
console.log(chalk_1.default.red(pkg.packageName) +
' has not been built yet. Unbuilt dependencies:\n' +
deps
.filter((dependency) => {
return !Array.from(done).some((p) => {
// console.log(p.packageName,dependency.packageName,p===dependency)
return p === dependency;
});
})
.map((p) => chalk_1.default.red('\t- ' +
((p === null || p === void 0 ? void 0 : p.packageName) ? p.packageName : p.toString()) +
'\n'))
.join(' '));
// console.log(chalk.red(pkg.packageName)+' has not been built yet. Built dependencies:\n' + deps.filter(dependency => {
// return Array.from(done).some(p => p.packageName === pkg.packageName)
// }).map(p => chalk.green('\t- '+p.packageName+'\n')).join(" "))
// console.log(chalk.red(pkg.packageName)+' has not been built yet. Built dependencies:\n' + deps.filter(dependency => done.has(pkg)).map(p => chalk.green('\t- '+p.packageName+'\n')).join(" "))
}
});
}
//if more to be built, iterate
if (stack.length > 0) {
return runStack(stack);
}
else {
onStackEnd(dependencies, results.filter(Boolean));
res();
}
};
//starts the process
if (startStack.length === 0) {
// No packages to build, resolve immediately
onStackEnd(dependencies, []);
res();
}
else {
runStack(startStack).catch((err) => {
rej(err);
});
}
return deferredPromise;
}
exports.runOnPackagesGroupedByDependencies = runOnPackagesGroupedByDependencies;
function hasDependency(pkg, childPkg, dependencies, depth = 1) {
console.log('Does ' + pkg.packageName + ' have dep ' + childPkg.packageName + ' ?');
let deps = dependencies.get(pkg);
if (deps.some((dependency) => {
console.log(dependency.packageName, childPkg.packageName, dependency === childPkg);
if (depth === 2)
return false;
// return dependency === childPkg;
return (dependency === childPkg ||
hasDependency(dependency, childPkg, dependencies, depth++));
})) {
console.log('##YES');
return true;
}
console.log('going up');
return false;
}
/**
* Finds the topmost package.json that could be an APP_ROOT
* Returns null if no app root is found (standalone repo case)
*/
function findAppRoot(startPath = process.cwd()) {
let currentPath = startPath;
let candidateRoots = [];
// Walk up the directory tree
for (let i = 0; i < 10; i++) {
const packageJsonPath = path_1.default.join(currentPath, 'package.json');
if (fs_extra_1.default.existsSync(packageJsonPath)) {
const packageJson = JSON.parse(fs_extra_1.default.readFileSync(packageJsonPath, 'utf8'));
candidateRoots.push({
path: currentPath,
hasWorkspaces: !!packageJson.workspaces,
isLincd: packageJson.lincd === true,
});
}
const parentPath = path_1.default.join(currentPath, '..');
// If we've reached the root or haven't moved up
if (parentPath === currentPath) {
break;
}
currentPath = parentPath;
}
// Find the topmost package.json that has workspaces
// Prefer non-lincd packages (app roots) over lincd packages
let appRoot = null;
for (let i = candidateRoots.length - 1; i >= 0; i--) {
const candidate = candidateRoots[i];
if (candidate.hasWorkspaces && !candidate.isLincd) {
appRoot = candidate.path;
break;
}
}
// If no non-lincd workspace found, use the topmost workspace
if (!appRoot) {
for (let i = candidateRoots.length - 1; i >= 0; i--) {
if (candidateRoots[i].hasWorkspaces) {
appRoot = candidateRoots[i].path;
break;
}
}
}
return appRoot;
}
/**
* Filters packages to only include those in the dependency tree of the app root
*/
function filterPackagesByDependencyTree(allPackages, appRootPath) {
const appPackageJson = (0, utils_js_1.getPackageJSON)(appRootPath);
if (!appPackageJson) {
return allPackages;
}
const relevantPackages = new Map();
const packagesToCheck = new Set();
// Start with direct dependencies from app root
if (appPackageJson.dependencies) {
Object.keys(appPackageJson.dependencies).forEach((dep) => {
if (allPackages.has(dep)) {
packagesToCheck.add(dep);
}
});
}
// Recursively add dependencies
const processedPackages = new Set();
while (packagesToCheck.size > 0) {
const packageName = Array.from(packagesToCheck)[0];
packagesToCheck.delete(packageName);
if (processedPackages.has(packageName)) {
continue;
}
processedPackages.add(packageName);
const packageDetails = allPackages.get(packageName);
if (packageDetails) {
relevantPackages.set(packageName, packageDetails);
// Get this package's dependencies
const packageJson = (0, utils_js_1.getPackageJSON)(packageDetails.path);
if (packageJson && packageJson.dependencies) {
Object.keys(packageJson.dependencies).forEach((dep) => {
if (allPackages.has(dep) && !processedPackages.has(dep)) {
packagesToCheck.add(dep);
}
});
}
}
}
return relevantPackages;
}
function buildAll(options) {
console.log('Building all LINCD packages of this repository in order of dependencies');
let lincdPackages = getLocalLincdPackageMap();
const originalPackageCount = lincdPackages.size;
// Check if we're in an app context and filter packages accordingly
const appRoot = findAppRoot();
if (appRoot) {
const appPackageJson = (0, utils_js_1.getPackageJSON)(appRoot);
// Check if this is an app (not a lincd package itself) with lincd dependencies
const isAppWithLincdDeps = appPackageJson &&
appPackageJson.lincd !== true &&
appPackageJson.dependencies &&
Object.keys(appPackageJson.dependencies).some((dep) => lincdPackages.has(dep));
if (isAppWithLincdDeps) {
(0, utils_js_1.debugInfo)(chalk_1.default.blue(`Found app root at: ${appRoot}`));
const filteredPackages = filterPackagesByDependencyTree(lincdPackages, appRoot);
console.log(chalk_1.default.magenta(`Found ${originalPackageCount} total LINCD packages, building only ${filteredPackages.size} that are relevant to this app`));
lincdPackages = filteredPackages;
}
else {
(0, utils_js_1.debugInfo)(chalk_1.default.blue(`Building all ${originalPackageCount} packages from workspace`));
}
}
else {
(0, utils_js_1.debugInfo)(chalk_1.default.blue(`No workspace root found, building all ${originalPackageCount} packages`));
}
let startFrom;
//by default start building
let building = true;
let from = options === null || options === void 0 ? void 0 : options.from;
let sync = (options === null || options === void 0 ? void 0 : options.sync) || false;
// console.log('from', from);
// console.log('sync', sync);
// process.exit();
//option to start from a specific package in the stack
if (from) {
startFrom = from;
//if we have a startFrom, then we havnt started the build process yet
building = startFrom ? false : true;
//clear targets
// target = '';
// target2 = '';
console.log(chalk_1.default.blue('Will skip builds until ' + startFrom));
// return async (pkg) => {};
}
// if (target2 == 'from') {
// startFrom = target3;
// //if we have a startFrom, then we havnt started the build process yet
// building = startFrom ? false : true;
//
// //clear targets
// target2 = '';
// target3 = '';
// console.log(chalk.blue('Will skip builds until ' + startFrom));
//
// // return async (pkg) => {};
// }
let done = new Set();
let failedModules = [];
progressUpdate(lincdPackages.size + ' packages left');
let packagesLeft = lincdPackages.size;
// let packagesLeft = lincdPackages.size - done.size;
runOnPackagesGroupedByDependencies(lincdPackages, (packageGroup, dependencies) => {
if (done.size > 0) {
(0, utils_js_1.debugInfo)(chalk_1.default.magenta('\n-------\nThese packages are next, since all their dependencies have now been build:'));
// log(stack);
}
(0, utils_js_1.debugInfo)('Now building: ' + chalk_1.default.blue(packageGroup.map((i) => i.packageName)));
return async (pkg) => {
let command;
let skipping = false;
//if we're skipping builds until a certain package
if (!building) {
//if the package name matches the package we're supposed to start from then start building packages
if (pkg.packageName == startFrom || pkg.packageName == startFrom) {
building = true;
}
//else still waiting for the package
else {
log(chalk_1.default.blue('skipping ' + pkg.packageName));
command = Promise.resolve(true);
skipping = true;
}
}
//unless told otherwise, build the package
if (!command) {
command = (0, exports.buildPackage)(null, null, path_1.default.join(process.cwd(), pkg.path), false);
// command = execPromise(
// 'cd ' + pkg.path + ' && yarn exec lincd build',
// // (target ? ' ' + target : '') +
// // (target2 ? ' ' + target2 : ''),
// false,
// false,
// {},
// false,
// );
log(chalk_1.default.cyan('Building ' + pkg.packageName));
process.stdout.write(packagesLeft + ' packages left\r');
}
return command
.then((res) => {
//empty string or true is success
//false is success with warnings
//any other string is the build error text
//undefined result means it failed
// if (res !== '' && res !== true && res !== false) {
if (typeof res === 'undefined') {
failedModules.push(pkg.packageName);
let dependentModules = getDependentPackages(dependencies, pkg);
if (dependentModules.length > 0) {
printBuildResults(failedModules, done);
console.log('Stopping build process because an error occurred whilst building ' +
pkg.packageName +
', which ' +
dependentModules.length +
' other packages depend on.'); //"+dependentModules.map(d => d.packageName).join(", ")));
log('Run ' +
chalk_1.default.greenBright(`lincd build-all --from=${pkg.packageName}`) +
' to build only the remaining packages'); //"+dependentModules.map(d => d.packageName).join(", ")));
process.exit(1);
}
}
else {
if (!skipping) {
log(chalk_1.default.green('Built ' + pkg.packageName) +
(res === false ? chalk_1.default.redBright(' (with warnings)') : ''));
}
done.add(pkg);
packagesLeft--;
// log(chalk.magenta(packagesLeft + ' packages left'));
process.stdout.write(packagesLeft + ' packages left\r');
if (packagesLeft == 0) {
printBuildResults(failedModules, done);
if (failedModules.length > 0) {
process.exit(1);
}
}
return res;
}
})
.catch(({ error, stdout, stderr }) => {
logError(chalk_1.default.red('Failed to build ' + pkg.packageName));
console.log(stdout);
process.exit(1);
// let dependentModules = getDependentP
});
//undefined result means it failed
/*if (typeof res === 'undefined')
{
// .catch(({ error,stdout,stderr }) => {
//this prints out the webpack output, including the build errors
// warn('Failed to build ' + pkg.packageName);
// console.log(stdout);
failedModules.push(pkg.packageName);
let dependentModules = getDependentPackages(dependencies,pkg);
if (dependentModules.length > 0)
{
printBuildResults(failedModules,done);
console.log(
'Stopping build process because an error occurred whilst building ' +
pkg.packageName +
', which ' +
dependentModules.length +
' other packages depend on.',
); //"+dependentModules.map(d => d.packageName).join(", ")));
log(
'Run ' +
chalk.greenBright(`lincd build-all --from=${pkg.packageName}`) +
' to build only the remaining packages',
); //"+dependentModules.map(d => d.packageName).join(", ")));
process.exit(1);
}
}
else //true is successful build, false is successful but with warnings
{
//successful build
// })
// .then((res) => {
if (!skipping)
{
log(chalk.green('Built ' + pkg.packageName)+(res === false ? chalk.redBright(' (with warnings)') : ''));
}
done.add(pkg);
packagesLeft--;
// log(chalk.magenta(packagesLeft + ' packages left'));
process.stdout.write(packagesLeft + ' packages left\r');
if (packagesLeft == 0)
{
printBuildResults(failedModules,done);
if (failedModules.length > 0)
{
process.exit(1);
}
}
return res;
}*/
// }).catch(err => {
// console.log(err);
// })
};
}, (dependencies) => {
//if no more packages to build but we never started building...
if (!building) {
console.log(chalk_1.default.red('Could not find the package to start from. Please provide a correct package name or package name to build from'));
}
else {
//Detecting cyclical dependencies that caused some packages not to be build
let first = true;
lincdPackages.forEach((pkg) => {
if (!done.has(pkg)) {
let deps = dependencies.get(pkg);
if (first) {
console.log(chalk_1.default.red('CYCLICAL DEPENDENCIES? - could not build some packages because they depend on each other.'));
first = false;
}
//print the cyclical dependencies
console.log(chalk_1.default.red(pkg.packageName) +
' depends on ' +
deps
.filter((dependency) => {
return typeof dependency !== 'string';
})
.map((d) => {
return done.has(d)
? d.packageName
: chalk_1.default.red(d.packageName);
})
.join(', '));
//also print some information why these packages have not been moved into the stack
let stringDependencies = deps.filter((d) => typeof d === 'string');
if (stringDependencies.length > 0) {
console.log(chalk_1.default.red('And it depends on these package(s) - which seem not to be proper packages :' +
stringDependencies.join(', ')));
console.log(chalk_1.default.red('Could you remove this from dependencies? Should it be a devDependency?'));
}
}
});
}
}, sync);
}
exports.buildAll = buildAll;
function getDependentPackages(dependencies, pkg) {
let dependentModules = [];
dependencies.forEach((dModuleDependencies, dModule) => {
if (dModuleDependencies.indexOf(pkg) !== -1) {
dependentModules.push(dModule);
}
});
return dependentModules;
}
/**
* Returns a map of the packages that this repository manages (so no packages found through the workspaces who's path contains ../ )
* @param rootPath
*/
function getLocalLincdPackageMap(rootPath = './') {
let map = new Map();
getLincdPackages(rootPath).forEach((pkg) => {
if (pkg.path.indexOf('../') === -1 && pkg.path.indexOf('..\\') === -1) {
// console.log(package.path);
map.set(pkg.packageName, pkg);
}
});
return map;
}
function getLocalLincdModules(rootPath = './') {
return getLincdPackages(rootPath).filter((pkg) => {
return pkg.path.indexOf('..\\') === -1;
});
}
function getLincdPackages(rootPath = process.cwd()) {
let pack = (0, utils_js_1.getPackageJSON)(rootPath);
if (!pack || !pack.workspaces) {
const originalRoot = rootPath;
for (let i = 0; i <= 3; i++) {
rootPath = path_1.default.join(originalRoot, ...Array(i).fill('..'));
pack = (0, utils_js_1.getPackageJSON)(rootPath);
if (pack && pack.workspaces) {
// log('Found workspace at '+packagePath);
break;
}
}
}
if (!pack || !pack.workspaces) {
warn(chalk_1.default.red('Could not find package workspaces. Make sure you run this command from a yarn workspace.'));
logHelp();
process.exit();
}
// console.log(pack.workspaces);
let res = [];
checkWorkspaces(rootPath, pack.workspaces, res);
return res;
}
exports.getLincdPackages = getLincdPackages;
function setVariable(name, replacement) {
//prepare name for regexp
name = name.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
variables[name] = replacement;
}
var replaceVariablesInFile = async (filePath) => {
var fileContent = await fs_extra_1.default.readFile(filePath, 'utf8').catch((err) => {
console.warn(chalk_1.default.red('Could not read file ' + filePath));
});
if (fileContent) {
var newContent = replaceCurlyVariables(fileContent);
return fs_extra_1.default.writeFile(filePath, newContent);
}
else {
return Promise.resolve();
}
};
var replaceCurlyVariables = function (string) {
// var reg = new RegExp('\\$\\{'+key+'\\}','g');
for (var key in variables) {
string = string.replace(new RegExp('\\$\\{' + key + '\\}', 'g'), variables[key]);
}
return string;
};
const capitalize = (str) => str.charAt(0).toUpperCase() + str.toLowerCase().slice(1);
const camelCase = (str) => {
let string = str.replace(/[^A-Za-z0-9]/g, ' ').split(' ');
if (string.length > 1) {
return string.reduce((result, word) => result + capitalize(word));
}
return str;
};
const createOntology = async (prefix, uriBase, basePath = process.cwd()) => {
if (!prefix) {
console.warn('Please provide a suggested prefix as the first argument');
return;
}
let sourceFolder = getSourceFolder(basePath);
let targetFolder = ensureFolderExists(sourceFolder, 'ontologies');
if (!uriBase) {
uriBase = 'http://lincd.org/ont/' + prefix + '/';
}
setVariable('uri_base', uriBase);
let { hyphenName, camelCaseName, underscoreName } = (0, exports.setNameVariables)(prefix);
//copy ontology accessor file
log("Creating files for ontology '" + prefix + "'");
let targetFile = path_1.default.join(targetFolder, hyphenName + '.ts');
fs_extra_1.default.copySync(path_1.default.join(dirname__, '..', '..', 'defaults', 'package', 'src', 'ontologies', 'example-ontology.ts'), targetFile);
//copy data files
let targetDataFile = path_1.default.join(targetFolder, '..', 'data', hyphenName + '.json');
let targetDataFile2 = path_1.default.join(targetFolder, '..', 'data', hyphenName + '.json.d.ts');
fs_extra_1.default.copySync(path_1.default.join(dirname__, '..', '..', 'defaults', 'package', 'src', 'data', 'example-ontology.json'), targetDataFile);
fs_extra_1.default.copySync(path_1.default.join(dirname__, '..', '..', 'defaults', 'package', 'src', 'data', 'example-ontology.json.d.ts'), targetDataFile2);
await replaceVariablesInFiles(targetFile, targetDataFile, targetDataFile2);
log(`Prepared a new ontology data files in ${chalk_1.default.magenta(targetDataFile.replace(basePath, ''))}`, `And an ontology accessor file in ${chalk_1.default.magenta(targetFile.replace(basePath, ''))}`);
//if this is not a lincd app (but a lincd package instead)
if (!sourceFolder.includes('frontend')) {
//then also add an import to index
let indexPath = addLineToIndex(`import './ontologies/${hyphenName}.js';`, 'ontologies');
log(`Added an import of this file from ${chalk_1.default.magenta(indexPath)}`);
}
};
exports.createOntology = createOntology;
const addLineToIndex = function (line, insertMatchString, root = process.cwd(), insertAtStart = false) {
//import ontology in index
let indexPath = ['index.ts', 'index.tsx']
.map((f) => path_1.default.join(root, 'src', f))
.find((indexFileName) => {
return fs_extra_1.default.existsSync(indexFileName);
});
if (indexPath) {
let indexContents = fs_extra_1.default.readFileSync(indexPath, 'utf-8');
let lines = indexContents.split(/\n/g);
let newContents;
for (var key in lines) {
//if the match string is found
if (lines[key].indexOf(insertMatchString) !== -1) {
//add the new line after this line
lines[key] += `\n${line}`;
newContents = lines.join('\n');
// log("Found at "+key,lines,newContents);
break;
}
}
if (!newContents) {
if (insertAtStart) {
newContents = `${line}\n${indexContents}`;
}
else {
newContents = `${indexContents}\n${line}`;
}
// log("Added at end",newContents);
}
fs_extra_1.default.writeFileSync(indexPath, newContents);
}
return indexPath;
};
const replaceVariablesInFiles = function (...files) {
return Promise.all(files.map((file) => {
return replaceVariablesInFile(file);
}));
};
const replaceVariablesInFolder = async function (folder) {
//get all files in folder, including files that start with a dot
try {
const files = await (0, glob_1.glob)(folder + '/**/*', { dot: true, nodir: true });
console.log('Replacing variables in files', files.join(', '));
await Promise.all(files.map((file) => {
return replaceVariablesInFile(file);
}));
}
catch (err) {
console.log('Error', err);
throw err;
}
};
const replaceVariablesInFilesWithRoot = function (root, ...files) {
return replaceVariablesInFiles(...files.map((f) => path_1.default.join(root, f)));
};
const hasYarnInstalled = async function () {
let version = (await (0, utils_js_1.execPromise)('yarn --version').catch((err) => {
console.log('yarn probably not working');
return '';
}));
return version.toString().match(/[0-9]+/);
};
const ensureFolderExists = function (...folders) {
let target;
folders.forEach((folder) => {
target = target ? path_1.default.join(target, folder) : path_1.default.join(folder);
if (!fs_extra_1.default.existsSync(target)) {
fs_extra_1.default.mkdirSync(target);
}
});
return target;
/*let targetFolder = path.join(...folders);
let parentDirectory = folders.slice(0, folders.length - 1);
if (!fs.existsSync(targetFolder)) {
if (fs.existsSync(path.join(...parentDirectory))) {
fs.mkdirSync(targetFolder);
} else {
warn(
`Please run this command from the root of your package. This command expects ${parentDirectory.toString()} to exists from that folder`,
);
}
}
return targetFolder;*/
};
const setNameVariables = function (name) {
let hyphenName = name.toLowerCase().replace(/[-_\s]+/g, '-');
let camelCaseName = camelCase(name); //some-package --> someModule
let underscoreName = name.replace(/[-\s]+/g, '_');
let plainName = name.replace(/[-\s]+/g, '');
//longer similar variables names should come before the shorter ones
setVariable('underscore_name', underscoreName);
setVariable('hyphen_name', hyphenName);
setVariable('camel_name', camelCaseName);
setVariable('name', name);
setVariable('plain_name', plainName);
return { hyphenName, camelCaseName, underscoreName, plainName };
};
exports.setNameVariables = setNameVariables;
function getSourceFolder(basePath = process.cwd()) {
//LINCD App
if (fs_extra_1.default.existsSync(path_1.default.join(basePath, 'frontend', 'src'))) {
return path_1.default.join(basePath, 'frontend', 'src');
}
//LINCD package
if (fs_extra_1.default.existsSync(path_1.default.join(basePath, 'src'))) {
return path_1.default.join(basePath, 'src');
}
else {
console.warn('Cannot find source folder');
return path_1.default.join(basePath, 'src');
}
}
/**
* get __dirname for either ESM/CJS
*/
const getScriptDir = () => {
return dirname__;
// // @ts-ignore
// if (typeof __dirname !== 'undefined')
// {
// // @ts-ignore
// return __dirname;
// }
// else
// {
// // @ts-ignore
// return dirname(import.meta.url).replace('file:/','');
// }
};
exports.getScriptDir = getScriptDir;
const createShape = async (name, basePath = process.cwd()) => {
let sourceFolder = getSourceFolder(basePath);
let targetFolder = ensureFolderExists(sourceFolder, 'shapes');
let { hyphenName, camelCaseName, underscoreName } = (0, exports.setNameVariables)(name);
//copy default shape file
// log("Creating files for shape '" + name + "'");
let targetFile = path_1.default.join(targetFolder, hyphenName + '.ts');
fs_extra_1.default.copySync(path_1.default.join((0, exports.getScriptDir)(), '..', '..', 'defaults', 'shape.ts'), targetFile);
//replace variables in some of the copied files
await replaceVariablesInFiles(targetFile);
log(`Created a new shape class template in ${chalk_1.default.magenta(targetFile.replace(basePath, ''))}`);
//if this is NOT a lincd app (but a lincd package)
let indexPath;
if (!sourceFolder.includes('frontend')) {
indexPath = addLineToIndex(`import './shapes/${hyphenName}.js';`, 'shapes');
log(`Added an import of this file from ${chalk_1.default.magenta(indexPath)}`);
}
};
exports.createShape = createShape;
const createSetComponent = async (name, basePath = process.cwd()) => {
let targetFolder = ensureFolderExists(basePath, 'src', 'components');
let { hyphenName, camelCaseName, underscoreName } = (0, exports.setNameVariables)(name);
//copy default shape file
log("Creating files for set component '" + name + "'");
let targetFile = path_1.default.join(targetFolder, hyphenName + '.tsx');
fs_extra_1.default.copySync(path_1.default.join((0, exports.getScriptDir)(), '..', '..', 'defaults', 'set-component.tsx'), targetFile);
let targetFile2 = path_1.default.join(targetFolder, hyphenName + '.scss');
fs_extra_1.default.copySync(path_1.default.join((0, exports.getScriptDir)(), '..', '..', 'defaults', 'component.scss'), targetFile2);
//replace variables in some of the copied files
await replaceVariablesInFiles(targetFile, targetFile2);
let indexPath = addLineToIndex(`import './components/${hyphenName}.js';`, 'components');
log(`Created a new set component in ${chalk_1.default.magenta(targetFile.replace(basePath, ''))}`, `Created a new stylesheet in ${chalk_1.default.magenta(targetFile2.replace(basePath, ''))}`, `Added an import of this file from ${chalk_1.default.magenta(indexPath)}`);
};
exports.createSetComponent = createSetComponent;
const createComponent = async (name, basePath = process.cwd()) => {
let sourceFolder = getSourceFolder(basePath);
let targetFolder = ensureFolderExists(sourceFolder, 'components');
let { hyphenName, camelCaseName, underscoreName } = (0, exports.setNameVariables)(name);
//copy default shape file
log("Creating files for component '" + name + "'");
let targetFile = path_1.default.join(targetFolder, hyphenName + '.tsx');
fs_extra_1.default.copySync(path_1.default.join((0, exports.getScriptDir)(), '..', 'defaults', 'component.tsx'), targetFile);
let targetFile2 = path_1.default.join(targetFolder, hyphenName + '.scss');
fs_extra_1.default.copySync(path_1.default.join((0, exports.getScriptDir)(), '..', 'defaults', 'component.scss'), targetFile2);
//replace variables in some of the copied files
await replaceVariablesInFiles(targetFile, targetFile2);
log(`Created a new component template in ${chalk_1.default.magenta(targetFile.replace(basePath, ''))}`, `Created component stylesheet template in ${chalk_1.default.magenta(targetFile2.replace(basePath, ''))}`);
//if this is not a lincd app (but a lincd package instead)
if (!sourceFolder.includes('frontend')) {
//then also add an import to index
let indexPath = addLineToIndex(`import './components/${hyphenName}.js';`, 'components');
log(`Added an import of this file from ${chalk_1.default.magenta(indexPath)}`);
}
};
exports.createComponent = createComponent;
//read the source of all ts/tsx files in the src folder
//if there is an import that imports a lincd package with /src/ in it, then warn
//if there is an import that imports something from outside the src folder, then warn
const checkImports = async (sourceFolder = getSourceFolder(), depth = 0, // Used to check if the import is outside the src folder
invalidImports = new Map()) => {
const dir = fs_extra_1.default.readdirSync(sourceFolder);
// Start checking each file in the source folder
for (const file of dir) {
const filename = path_1.default.join(sourceFolder, file);
// File is either a directory, or not a .ts(x)
// INFO: For future use - if this part fails, it could be due to user permissions
// i.e. the program not having access to check the file metadata
if (!filename.match(/\.tsx?$/)) {
try {
if ((0, fs_1.statSync)(filename).isDirectory()) {
await (0, exports.checkImports)(filename, depth + 1, invalidImports);
}
else {
// Ignore all files that aren't one of the following:
// - .ts
// - .tsx
continue;
}
}
catch (e) {
console.log(e);
}
}
const allImports = await (0, utils_js_1.getFileImports)(filename);
if (!invalidImports.has(filename)) {
invalidImports.set(filename, []);
}
allImports.forEach((i) => {
if ((0, utils_js_1.isImportOutsideOfPackage)(i, depth)) {
invalidImports.get(filename).push({
type: 'outside_package',
importPath: i,
});
}
if ((0, utils_js_1.isInvalidLINCDImport)(i, depth)) {
invalidImports.get(filename).push({
type: 'lincd',
importPath: i,
});
}
if ((0, utils_js_1.isImportWithMissingExtension)(i)) {
invalidImports.get(filename).push({
type: 'missing_extension',
importPath: i,
});
}
});
}
let res = '';
//check if invalidImports has any
let flat = [...invalidImports.values()].flat();
// All recursion mus