UNPKG

lincd-cli

Version:

Command line tools for the lincd.js library

1,073 lines (1,071 loc) 98.8 kB
"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 () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __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.buildApp = exports.startServer = exports.ensureEnvironmentLoaded = exports.depCheck = exports.depCheckStaged = exports.checkImports = exports.createComponent = exports.createSetComponent = exports.createShape = exports.getScriptDir = exports.setNameVariables = exports.createOntology = exports.createApp = void 0; exports.warn = warn; exports.developPackage = developPackage; exports.buildAll = buildAll; exports.getLincdPackages = getLincdPackages; 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 utils_js_1 = require("./utils.js"); const fs_1 = require("fs"); const find_nearest_package_json_1 = require("find-nearest-package-json"); 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 staged_git_files_1 = __importDefault(require("staged-git-files")); const ora_1 = __importDefault(require("ora")); //@ts-ignore let dirname__ = typeof __dirname !== 'undefined' ? __dirname : (0, path_1.dirname)(import.meta.url).replace('file:/', ''); var variables = {}; const createApp = async (name, basePath = process.cwd()) => { if (!name) { console.warn('Please provide a name as the first argument'); } let { hyphenName, camelCaseName, underscoreName } = (0, exports.setNameVariables)(name); 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 \'' + name + '\''); //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.log(chalk_1.default.redBright('Warning: ') + message); // console.log(chalk.red(message)); }); } 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'); } } 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(); //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]; const runPackage = (runFunction, pck) => { return runFunction(pck) .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); }) .then((res) => { done.add(pck); return res; }); }; 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)); } }; //starts the process runStack(startStack); } 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; } function buildAll(options) { console.log('Building all LINCD packages of this repository in order of dependencies'); let lincdPackages = getLocalLincdPackageMap(); 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 }) => { warn(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); } 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)(); if (!pack || !pack.workspaces) { for (let i = 0; i <= 3; i++) { rootPath = path_1.default.join(process.cwd(), ...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; } 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 = function (folder) { //get all files in folder, including files that start with a dot glob_1.glob(folder + '/**/*', { dot: true, nodir: true }, async function (err, files) { if (err) { console.log('Error', err); } else { // console.log(files); await Promise.all(files.map((file) => { return replaceVariablesInFile(file); })); } }); }; 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.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 must have finished, display any errors if (depth === 0 && flat.length > 0) { res += chalk_1.default.red('Invalid imports found.\n'); invalidImports.forEach((value, key) => { // res += '- '+chalk.blueBright(key.split('/').pop()) + ':\n'; value.forEach(({ type, importPath }) => { let message = key.split('/').pop() + ' imports from \'' + importPath + '\''; if (type === 'outside_package') { message += ' which is outside the package source root'; } if (type === 'lincd') { message += ' which should not contain /src/ or /lib/ in the import path'; } if (type === 'missing_extension') { message += ' which should end with a file extension. Like .js or .scss'; } res += chalk_1.default.red(message + '\n'); }); }); throw res; // process.exit(1); } else if (depth === 0 && invalidImports.size === 0) { // console.info('All imports OK'); // process.exit(0); return true; } }; exports.checkImports = checkImports; const depCheckStaged = async () => { console.log('Checking dependencies of staged files'); (0, staged_git_files_1.default)(async function (err, results) { const packages = new Set(); await Promise.all(results.map(async (file) => { // console.log('STAGED: ', file.filename); let root = await (0, find_nearest_package_json_1.findNearestPackageJson)(file.filename); packages.add(root.path); })); [...packages].forEach((packageRoot) => { const pack = JSON.parse(fs_extra_1.default.readFileSync(packageRoot, 'utf8')); const srcPath = packageRoot.replace('package.json', ''); console.log('Checking dependencies of ' + chalk_1.default.blue(pack.name) + ':'); return (0, exports.depCheck)(process.cwd() + '/' + srcPath); // console.log('check dependencies of ' + pack.name); // // console.log('ROOT of ' + file.filename + ': ' + root.path); // console.log('ROOT of ' + file.filename + ': ' + root.data); }); }); }; exports.depCheckStaged = depCheckStaged; const depCheck = async (packagePath = process.cwd()) => { return new Promise((resolve, reject) => { (0, depcheck_1.default)(packagePath, {}, (results) => { if (results.missing) { let lincdPackages = getLocalLincdModules(); let missing = Object.keys(results.missing); //filter out missing types, if it builds we're not too concerned about that at the moment? //especially things like @types/react, @types/react-dom, @types/node (they are added elsewhere?) // missing = missing.filter(m => m.indexOf('@types/') === 0); //currently react is not an explicit dependency, but we should add it as a peer dependency missing.splice(missing.indexOf('react'), 1); let missingLincdPackages = missing.filter((missingDep) => { return lincdPackages.some((lincdPackage) => { return lincdPackage.packageName === missingDep; }); }); //currently just missing LINCD packages cause a hard failure exit code if (missingLincdPackages.length > 0) { reject(chalk_1.default.red(packagePath.split('/').pop() + '\n[ERROR] These LINCD packages are imported but they are not listed in package.json:\n- ' + missingLincdPackages.join(',\n- '))); } else if (missing.length > 0) { resolve(chalk_1.default.redBright('warning: ' + packagePath.split('/').pop() + ' is missing dependencies:\n - ' + missing.join('\n - '))); } else { resolve(true); } } // if(Object.keys(results.invalidFiles).length > 0) { // console.warn(chalk.red("Invalid files:\n")+Object.keys(results.invalidFiles).join(",\n")); // } // if(Object.keys(results.invalidDirs).length > 0) { // console.warn(chalk.red("Invalid dirs:\n")+results.invalidDirs.toString()); // } // if(results.unused) { // console.warn("Unused dependencies: "+results.missing.join(", ")); // } }); }); }; exports.depCheck = depCheck; const ensureEnvironmentLoaded = async () => { if (!process.env.NODE_ENV) { //load env-cmd for development environment let { GetEnvVars } = await Promise.resolve().then(() => __importStar(require('env-cmd'))); let envCmdrcPath = path_1.default.join(process.cwd(), '.env-cmdrc.json'); if (!fs_extra_1.default.existsSync(envCmdrcPath)) { console.warn('No .env-cmdrc.json found in this folder. Are you running this command from the root of a LINCD app?'); process.exit(); } let vars = await GetEnvVars({ envFile: { filePath: envCmdrcPath, }, }); let environments = Object.keys(vars); //if _main is present, load it first if (environments.includes('_main')) { process.env = { ...process.env, ...vars._main }; } //if --env is passed, load that environment let args = process.argv.splice(2); if (args.includes('--env')) { let envIndex = args.indexOf('--env'); let env = args[envIndex + 1]; env.split(',').forEach((singleEnvironment) => { if (environments.includes(singleEnvironment)) { console.log('Environment: ' + singleEnvironment); process.env = { ...process.env, ...vars[singleEnvironment] }; } else { console.warn('Environment ' + singleEnvironment + ' not found in .env-cmdrc.json. Available environments: ' + environments.join(', ')); } }); } else { //chose development by default process.env = { ...process.env, ...vars.development }; console.log('No environment specified, using development'); } } }; exports.ensureEnvironmentLoaded = ensureEnvironmentLoaded; const startServer = async (initOnly = false, ServerClass = null) => { await (0, exports.ensureEnvironmentLoaded)(); let lincdConfig = (await Promise.resolve(`${path_1.default.join(process.cwd(), 'lincd.config.js')}`).then(s => __importStar(require(s)))).default; // function scssLoadcall(source, filename) { // return 'console.log("SCSS CALL: ' + filename + '");\n' + source; // process.exit(); // } // hook.hook('.scss', scssLoadcall); // hook.hook('.css', scssLoadcall); // import.meta. // // hook.hook('*.css', scssLoadcall); // // hook.hook('Body.module.css', scssLoadcall); // hook.hook('.module.css', scssLoadcall); if (!ServerClass) { //@ts-ignore ServerClass = (await Promise.resolve().then(() => __importStar(require('lincd-server/shape