mirror of
https://github.com/iconify/iconify.git
synced 2024-11-09 14:50:56 +00:00
More options for monorepo script, build and test all packages
This commit is contained in:
parent
93ccc6c439
commit
a50939f438
22
README.md
22
README.md
@ -69,7 +69,7 @@ Other packages:
|
||||
|
||||
## Installation
|
||||
|
||||
This monorepo used Lerna to manage packages, but due to few bugs in Lerna, it was replaced with custom manager.
|
||||
This monorepo used Lerna to manage packages, but due to several bugs in Lerna and Lerna development being abandoned, it was replaced with custom manager.
|
||||
|
||||
To install dependencies in all packages, run
|
||||
|
||||
@ -85,6 +85,26 @@ If you want to remove `node_modules` for all packages, run `npm run clean`.
|
||||
|
||||
If you want to re-install dependencies, run `npm run reinstall`.
|
||||
|
||||
To build everything, run `npm run build` (this excludes demo packages).
|
||||
|
||||
To run tests, run `npm run test` (this excludes demo packages).
|
||||
|
||||
### Other commands
|
||||
|
||||
You can run any commands on any package from that package's directory.
|
||||
|
||||
Commands that modify `node_modules` might break symlinks. To fix it, run `npm run link` from monorepo directory.
|
||||
|
||||
### Commands for all packages
|
||||
|
||||
If you want to run a command on all packages, run `node monorepo run your_command --if-present`.
|
||||
|
||||
There are several options to filter packages:
|
||||
|
||||
- `--if-present` will check if command is present before running it.
|
||||
- `--public` will execute command only for public packages (everything except demo).
|
||||
- `--private` will execute command only for private packages (only demo packages).
|
||||
|
||||
### Monorepo on Windows
|
||||
|
||||
This monorepo uses symbolic links to create links between packages. This allows development of multiple packages at the same time.
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { consoleLog } from './log';
|
||||
import { PackageInfo } from './types';
|
||||
import { findWorkspaces } from './workspaces';
|
||||
import { filterWorkspaces } from './workspaces';
|
||||
|
||||
/**
|
||||
* Run callback for all workspaces
|
||||
@ -8,10 +9,10 @@ export function runAction(
|
||||
log: string,
|
||||
callback: (workspace: PackageInfo) => void
|
||||
): void {
|
||||
const workspaces = findWorkspaces();
|
||||
const workspaces = filterWorkspaces();
|
||||
if (!workspaces.length) {
|
||||
throw new Error('No packages found');
|
||||
}
|
||||
console.log(`${log}...`);
|
||||
consoleLog(`${log}...`);
|
||||
workspaces.forEach(callback);
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import fs from 'fs';
|
||||
import { addToPath, pathToString, relativePath } from './dirs';
|
||||
import { consoleLog } from './log';
|
||||
import { PackageInfo } from './types';
|
||||
|
||||
/**
|
||||
@ -17,7 +18,7 @@ export function cleanWorkspace(workspace: PackageInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Removing:', relativePath(dir));
|
||||
consoleLog('Removing:', relativePath(dir));
|
||||
try {
|
||||
fs.rmSync(dir, {
|
||||
recursive: true,
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { spawnSync } from 'child_process';
|
||||
import { pathToString, relativePath } from './dirs';
|
||||
import { consoleError, consoleLog } from './log';
|
||||
import { actionOptions } from './options';
|
||||
import { PackageInfo } from './types';
|
||||
|
||||
let npm: string;
|
||||
@ -7,7 +9,7 @@ let npm: string;
|
||||
/**
|
||||
* Get NPM command
|
||||
*/
|
||||
function getNPMCommand(): string {
|
||||
function getNPMCommand(): string {
|
||||
const clients = ['npm', 'npm.cmd'];
|
||||
for (let i = 0; i < clients.length; i++) {
|
||||
const cmd = clients[i];
|
||||
@ -16,10 +18,10 @@ let npm: string;
|
||||
return cmd;
|
||||
}
|
||||
}
|
||||
throw new Error('Cannot execute NPM commands')
|
||||
consoleError('Cannot detect NPM client');
|
||||
process.exit(5);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Run NPM command
|
||||
*/
|
||||
@ -29,14 +31,15 @@ export function runNPMCommand(workspace: PackageInfo, params: string[]): void {
|
||||
}
|
||||
|
||||
const cwd = pathToString(workspace.path);
|
||||
console.log('\n' + relativePath(cwd) + ':', npm, params.join(' '));
|
||||
consoleLog('\n' + relativePath(cwd) + ':', npm, params.join(' '));
|
||||
const result = spawnSync(npm, params, {
|
||||
cwd,
|
||||
stdio: 'inherit',
|
||||
stdio: actionOptions.silent ? 'pipe' : 'inherit',
|
||||
});
|
||||
if (result.status !== 0) {
|
||||
throw new Error(
|
||||
consoleError(
|
||||
`Failed to run "${npm} ${params.join(' ')}" at ${relativePath(cwd)}`
|
||||
);
|
||||
process.exit(result.status);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import fs from 'fs';
|
||||
import { dirname } from 'path';
|
||||
import { addToPath, pathToString, relativePath } from './dirs';
|
||||
import { consoleLog } from './log';
|
||||
import type { PathList } from './types';
|
||||
|
||||
/**
|
||||
@ -24,7 +25,7 @@ export function createLink(from: PathList, to: PathList, unlink = true): void {
|
||||
}
|
||||
|
||||
// Create link
|
||||
console.log(
|
||||
consoleLog(
|
||||
'Creating link:',
|
||||
relativePath(fromDir),
|
||||
'->',
|
||||
@ -38,7 +39,7 @@ export function createLink(from: PathList, to: PathList, unlink = true): void {
|
||||
*/
|
||||
export function removeLink(path: PathList, packageName: string): void {
|
||||
const dir = pathToString(addToPath(path, packageName));
|
||||
console.log('Removing link:', relativePath(dir));
|
||||
consoleLog('Removing link:', relativePath(dir));
|
||||
try {
|
||||
fs.unlinkSync(dir);
|
||||
} catch (err) {
|
||||
@ -63,7 +64,7 @@ function rmdir(dir: string) {
|
||||
try {
|
||||
const stat = fs.lstatSync(dir);
|
||||
if (stat.isDirectory() || stat.isSymbolicLink()) {
|
||||
console.log('Removing', relativePath(dir));
|
||||
consoleLog('Removing', relativePath(dir));
|
||||
fs.rmSync(dir, {
|
||||
recursive: true,
|
||||
});
|
||||
|
13
monorepo/src/helpers/log.ts
Normal file
13
monorepo/src/helpers/log.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { actionOptions } from './options';
|
||||
|
||||
export function consoleLog(...args: unknown[]) {
|
||||
if (!actionOptions.silent) {
|
||||
console.log(...args);
|
||||
}
|
||||
}
|
||||
|
||||
export function consoleError(...args: unknown[]) {
|
||||
if (!actionOptions.silent) {
|
||||
console.error(...args);
|
||||
}
|
||||
}
|
41
monorepo/src/helpers/options.ts
Normal file
41
monorepo/src/helpers/options.ts
Normal file
@ -0,0 +1,41 @@
|
||||
type PackageTypeFilter = 'private' | 'public' | 'all';
|
||||
|
||||
/**
|
||||
* Interface for options
|
||||
*/
|
||||
export interface ActionOptions {
|
||||
// Run command if present, applies to 'run' and 'run-script'
|
||||
ifPresent: boolean;
|
||||
|
||||
// Filter packages by `private` property, undefined if not set
|
||||
private?: PackageTypeFilter;
|
||||
|
||||
// Silent
|
||||
silent: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options
|
||||
*/
|
||||
export const actionOptions: ActionOptions = {
|
||||
ifPresent: false,
|
||||
silent: false,
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggle private filter
|
||||
*/
|
||||
export function enablePrivateFilter(value: boolean) {
|
||||
const newValue: PackageTypeFilter = value ? 'private' : 'public';
|
||||
|
||||
if (!actionOptions.private) {
|
||||
// Not set: set to new value
|
||||
actionOptions.private = newValue;
|
||||
return;
|
||||
}
|
||||
|
||||
if (actionOptions.private !== newValue) {
|
||||
// Enable all
|
||||
actionOptions.private = 'all';
|
||||
}
|
||||
}
|
@ -10,27 +10,33 @@ import { pathToString } from './dirs';
|
||||
export function getPackageInfo(path: PathList): PackageInfo | null {
|
||||
const packageFilename = pathToString(addToPath(path, 'package.json'));
|
||||
|
||||
let name: unknown;
|
||||
let version: string;
|
||||
let isPrivate: boolean;
|
||||
let data: Record<string, unknown>;
|
||||
let name: string;
|
||||
try {
|
||||
const data = JSON.parse(
|
||||
fs.readFileSync(packageFilename, 'utf8')
|
||||
) as Record<string, unknown>;
|
||||
data = JSON.parse(fs.readFileSync(packageFilename, 'utf8'));
|
||||
if (
|
||||
typeof data !== 'object' ||
|
||||
!data ||
|
||||
typeof data.name !== 'string'
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
name = data.name;
|
||||
version = typeof data.version === 'string' ? data.version : '';
|
||||
isPrivate = version ? !!data.private : true;
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
return typeof name === 'string'
|
||||
? {
|
||||
name,
|
||||
private: isPrivate,
|
||||
version,
|
||||
path,
|
||||
}
|
||||
: null;
|
||||
|
||||
const version = typeof data.version === 'string' ? data.version : '';
|
||||
const isPrivate = version ? !!data.private : true;
|
||||
const scripts =
|
||||
typeof data.scripts === 'object' ? Object.keys(data.scripts) : [];
|
||||
return {
|
||||
name,
|
||||
private: isPrivate,
|
||||
version,
|
||||
scripts,
|
||||
path,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -15,5 +15,6 @@ export interface PackageInfo {
|
||||
name: PackageName;
|
||||
private: boolean;
|
||||
version: string;
|
||||
scripts: string[];
|
||||
path: PathList;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import type { PathList, PackageInfo } from './types';
|
||||
import { rootDir } from './dirs';
|
||||
import { addToPath, findSubdirs } from './dirs';
|
||||
import { getFixPackageName, getPackageInfo } from './package';
|
||||
import { actionOptions } from './options';
|
||||
|
||||
/**
|
||||
* Workspaces cache
|
||||
@ -60,3 +61,31 @@ export function findWorkspaces(): PackageInfo[] {
|
||||
// Cache and return result
|
||||
return workspaces;
|
||||
}
|
||||
|
||||
// Cache for filterWorkspaces() result
|
||||
let filteredWorkspaces: PackageInfo[];
|
||||
|
||||
/**
|
||||
* Get only workspaces that match options
|
||||
*/
|
||||
export function filterWorkspaces(): PackageInfo[] {
|
||||
if (!filteredWorkspaces) {
|
||||
filteredWorkspaces = findWorkspaces().filter((item) => {
|
||||
// Filter by `private` property
|
||||
if (
|
||||
actionOptions.private !== void 0 &&
|
||||
actionOptions.private !== 'all'
|
||||
) {
|
||||
if (item.private !== (actionOptions.private === 'private')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: match name
|
||||
|
||||
// Match
|
||||
return true;
|
||||
});
|
||||
}
|
||||
return filteredWorkspaces;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import { runAction } from './helpers/action';
|
||||
import { addLinksToWorkspace } from './helpers/add-links';
|
||||
import { cleanWorkspace } from './helpers/clean';
|
||||
import { runNPMCommand } from './helpers/exec';
|
||||
import { actionOptions, enablePrivateFilter } from './helpers/options';
|
||||
import { removeLinksFromWorkspace } from './helpers/remove-links';
|
||||
|
||||
/**
|
||||
@ -35,11 +36,19 @@ interface ActionWithParam {
|
||||
const actionWithParamsFunctions: Record<string, (param: string) => void> = {
|
||||
run: (param: string) => {
|
||||
runAction(`Running "npm run ${param}"`, (workspace) => {
|
||||
runNPMCommand(workspace, ['run', param]);
|
||||
if (
|
||||
!actionOptions.ifPresent ||
|
||||
workspace.scripts.indexOf(param) !== -1
|
||||
) {
|
||||
runNPMCommand(workspace, ['run', param]);
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
// Aliases
|
||||
actionWithParamsFunctions['run-script'] = actionWithParamsFunctions['run'];
|
||||
|
||||
/**
|
||||
* Run code
|
||||
*/
|
||||
@ -60,6 +69,25 @@ export function run() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Options
|
||||
switch (arg) {
|
||||
case '--if-present':
|
||||
actionOptions.ifPresent = true;
|
||||
return;
|
||||
|
||||
case '--silent':
|
||||
actionOptions.silent = true;
|
||||
return;
|
||||
|
||||
case '--public':
|
||||
enablePrivateFilter(false);
|
||||
return;
|
||||
|
||||
case '--private':
|
||||
enablePrivateFilter(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Action
|
||||
if (actionFunctions[arg] !== void 0) {
|
||||
actions.push(arg);
|
||||
|
13
package.json
13
package.json
@ -4,6 +4,7 @@
|
||||
"description": "The most versatile icon framework",
|
||||
"author": "Vjacheslav Trushkin <cyberalien@gmail.com> (https://iconify.design)",
|
||||
"license": "(Apache-2.0 OR GPL-2.0)",
|
||||
"main": "./monorepo/index.js",
|
||||
"bugs": "https://github.com/iconify/iconify/issues",
|
||||
"homepage": "https://iconify.design/",
|
||||
"repository": {
|
||||
@ -11,11 +12,13 @@
|
||||
"url": "https://github.com/iconify/iconify.git"
|
||||
},
|
||||
"scripts": {
|
||||
"install": "node monorepo/index install",
|
||||
"clean": "node monorepo/index clean",
|
||||
"link": "node monorepo/index link",
|
||||
"unlink": "node monorepo/index unlink",
|
||||
"reinstall": "node monorepo/index clean install"
|
||||
"install": "node monorepo install",
|
||||
"clean": "node monorepo clean",
|
||||
"link": "node monorepo link",
|
||||
"unlink": "node monorepo unlink",
|
||||
"reinstall": "node monorepo clean install",
|
||||
"build": "node monorepo run build --if-present --public",
|
||||
"test": "node monorepo run build --if-present --public"
|
||||
},
|
||||
"devDependencies": {}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user