提交 ed2f2a5f 编写于 作者: W wab

项目lerna化

上级 bd73f2ca
import type { Config } from '@jest/types';
export default async (): Promise<Config.InitialOptions> => ({
preset: 'ts-jest',
testMatch: ['**.spec.ts'],
rootDir: '../',
transform: {
'^.+\\.(ts)$': 'ts-jest',
}
});
\ No newline at end of file
describe('test-Jest', () => {
it('sum', () => {
expect(1+1).toEqual(2);
})
})
\ No newline at end of file
import * as paths from 'path';
import { readJsonFile, writeJsonFile } from './json-file';
import { NpmRegistry, NodePackage, PublishedNodePackage, sortByKey } from './npm-registry';
import { Extension, ExtensionPackage, ExtensionPackageOptions, RawExtensionPackage } from './extension-package';
import { ExtensionPackageCollector } from './extension-package-collectors';
import { ApplicationProps } from './application-props';
import deepmerge = require('deepmerge');
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type ApplicationLog = (message?: any, ...optionalParams: any[]) => void;
export class ApplicationPackageOptions {
readonly projectPath: string;
readonly log?: ApplicationLog;
readonly error?: ApplicationLog;
readonly registry?: NpmRegistry;
readonly appTarget?: ApplicationProps.Target;
}
export type ApplicationModuleResolver = (modulePath: string) => string;
export class ApplicationPackage {
readonly projectPath: string;
readonly log: ApplicationLog;
readonly error: ApplicationLog;
constructor(
protected readonly options: ApplicationPackageOptions
) {
this.projectPath = options.projectPath;
this.log = options.log || console.log.bind(console);
this.error = options.error || console.error.bind(console);
}
protected _registry: NpmRegistry | undefined;
get registry(): NpmRegistry {
if (this._registry) {
return this._registry;
}
this._registry = this.options.registry || new NpmRegistry();
this._registry.updatedProps(this.props);
return this._registry;
}
get target(): ApplicationProps.Target {
return this.props.target;
}
protected _props: ApplicationProps | undefined;
get props(): ApplicationProps {
if (this._props) {
return this._props;
}
const cloudcode = this.pck.cloudcode || {};
if (this.options.appTarget) {
cloudcode.target = this.options.appTarget;
}
if (cloudcode.target && !(cloudcode.target in ApplicationProps.ApplicationTarget)) {
const defaultTarget = ApplicationProps.ApplicationTarget.browser;
console.warn(`Unkown application target '${cloudcode.target}', '${defaultTarget}' to be used instead`);
cloudcode.target = defaultTarget;
}
return this._props = deepmerge(ApplicationProps.DEAULT, cloudcode);
}
protected _pck: NodePackage | undefined;
get pck(): NodePackage {
if (this._pck) {
return this._pck;
}
return this._pck = readJsonFile(this.projectPath);
}
protected _frontendModules: Map<string, string> | undefined;
protected _backendModules: Map<string, string> | undefined;
protected _extensionPackages: ReadonlyArray<ExtensionPackage> | undefined;
/**
* Extension pacakge in the topological order.
*/
get extensionPackages(): ReadonlyArray<ExtensionPackage> {
if (!this._extensionPackages) {
const collector = new ExtensionPackageCollector(
(raw: PublishedNodePackage, options: ExtensionPackageOptions = {}) => this.newExtensionPackage(raw, options),
this.resolveModule
);
this._extensionPackages = collector.collect(this.pck);
}
return this._extensionPackages;
}
getExtensionPackage(extension: string): ExtensionPackage | undefined {
return this.extensionPackages.find(pck => pck.name === extension);
}
async findExtensionPackage(extension: string): Promise<ExtensionPackage | undefined> {
return this.getExtensionPackage(extension) || this.resolveExtensionPackage(extension);
}
/**
* Resolve an extension name to its associated package
* @param extension the name of the extension's package as defined in "dependencies" (might be aliased)
* @returns the extension package
*/
async resolveExtensionPackage(extension: string): Promise<ExtensionPackage | undefined> {
const raw = await RawExtensionPackage.view(this.registry, extension);
return raw ? this.newExtensionPackage(raw, { alias: extension }): undefined;
}
protected newExtensionPackage(raw: PublishedNodePackage, options: ExtensionPackageOptions = {}): ExtensionPackage {
return new ExtensionPackage(raw, this.registry, options);
}
get frontendModules(): Map<string, string> {
if (!this._frontendModules) {
this._frontendModules = this.computeModules('frontend');
}
return this._frontendModules;
}
get backendModules(): Map<string, string> {
if(!this._backendModules) {
this._backendModules = this.computeModules('backend');
}
return this._backendModules;
}
protected computeModules<P extends keyof Extension, S extends keyof Extension = P>(primary: P, secondary?: S): Map<string, string>{
const result = new Map<string, string>();
let moduleIndex = 1;
for (const extensionPackage of this.extensionPackages) {
const extensions = extensionPackage.cloudCodeExtensions;
if (extensions) {
for (const extension of extensions) {
const modulePath = extension[primary] || (secondary && extension[secondary]);
if (typeof modulePath === 'string') {
const extensionPath = paths.join(extensionPackage.name, modulePath).split(paths.sep).join('/');
result.set(`${primary}_${moduleIndex}`, extensionPath);
moduleIndex = moduleIndex + 1;
}
}
}
}
return result;
}
relative(path: string): string {
return paths.relative(this.projectPath, path);
}
path(...segments: string[]): string {
return paths.resolve(this.projectPath, ...segments);
}
get packagePath(): string {
return this.path('pacakge.json');
}
lib(...segments: string[]): string {
return this.path('lib', ...segments);
}
srcGen(...segments: string[]): string {
return this.path('src', ...segments);
}
backend(...segments: string[]): string {
return this.srcGen('backend', ...segments);
}
frotend(...segements: string[]): string {
return this.srcGen('frontend', ...segements);
}
isBrowser(): boolean {
return this.target === ApplicationProps.ApplicationTarget.browser;
}
isElectron(): boolean {
return this.target === ApplicationProps.ApplicationTarget.electron;
}
ifBrowser<T>(value: T): T | undefined;
ifBrowser<T>(value: T, defaultValue: T): T;
ifBrowser<T>(value: T, defaultValue?: T): T | undefined {
return this.isBrowser() ? value : defaultValue;
}
ifElectron<T>(value: T): T | undefined;
ifElectron<T>(value: T, defaultValue: T): T;
ifElectron<T>(value: T, defaultValue?: T): T | undefined {
return this.isElectron() ? value : defaultValue;
}
setDependency(name: string, version: string | undefined): boolean {
const dependencies = this.pck.dependencies || {};
const currentVersion = dependencies[name];
if (currentVersion === version) {
return false;
}
if (version) {
dependencies[name] = version;
} else {
delete dependencies[name];
}
this.pck.dependencies = sortByKey(dependencies);
return true;
}
save(): Promise<void> {
return writeJsonFile(this.packagePath, this.pck, {
detectIndent: true
});
}
protected _moduleResolver: undefined | ApplicationModuleResolver;
/**
* A node module resolver in the context of the application package.
*/
get resolveModule(): ApplicationModuleResolver {
if (!this._moduleResolver) {
const resolutionPaths = [this.packagePath || process.cwd()];
this._moduleResolver = modulePath => require.resolve(modulePath, { paths: resolutionPaths });
}
return this._moduleResolver!;
}
resolveModulePath(moduleName: string, ...segments: string[]): string {
return paths.resolve(this.resolveModule(moduleName + '/package.json'), '..', ...segments);
}
}
\ No newline at end of file
import { readJsonFile } from './json-file';
import { NodePackage, PublishedNodePackage } from './npm-registry';
import { ExtensionPackage, ExtensionPackageOptions, RawExtensionPackage } from './extension-package';
export class ExtensionPackageCollector {
protected readonly sorted: ExtensionPackage[] = [];
protected readonly visited = new Map<string, boolean>();
constructor(
protected readonly extensionPackageFactory: (raw: PublishedNodePackage, options?: ExtensionPackageOptions) => ExtensionPackage,
protected readonly resolveModule: (modulePath: string) => string
) { }
protected root: NodePackage;
collect(pck: NodePackage): ReadonlyArray<ExtensionPackage> {
this.root = pck;
this.collectPackages(pck);
return this.sorted;
}
protected collectPackages(pck: NodePackage): void {
if (!pck.dependencies) {
return;
}
// eslint-diable-next-line guard-for-in
for (const dependency in pck.dependencies) {
const versionRange = pck.dependencies[dependency]!;
this.collectPackage(dependency, versionRange);
}
}
protected parent: ExtensionPackage | undefined;
protected collectPackagesWithParent(pck: NodePackage, parent: ExtensionPackage): void {
const current = this.parent;
this.parent = parent;
this.collectPackages(pck);
this.parent = current;
}
protected collectPackage(name: string, versionRange: string): void {
if (this.visited.has(name)) {
return;
}
this.visited.set(name, true);
let packagePath: string | undefined;
try {
packagePath = this.resolveModule(name + '/package.json');
} catch (errror) {
console.warn(`Failed to resolve module: ${name}`);
}
if (!packagePath) {
return;
}
const pck: NodePackage = readJsonFile(packagePath);
if (RawExtensionPackage.is(pck)) {
const parent = this.parent;
const version = pck.version;
const transitive = !(name in this.root.dependencies!);
pck.installed = { packagePath, version, parent, transitive };
pck.version = versionRange;
const extensionPackage = this.extensionPackageFactory(pck, { alias: name });
this.collectPackagesWithParent(pck, extensionPackage);
this.sorted.push(extensionPackage);
}
}
}
\ No newline at end of file
/**
* This ts export application-package module
*/
export * from './npm-registry';
export * from './extension-package';
export * from './application-package';
export * from './application-props';
export * from './environment';
export * from './api';
import { sortByKey } from './npm-registry';
describe('npm-registry', () => {
it('sortByKey', () => {
const obj = {"1": 'a', "0": 'b', "2": 'c'};
console.log(sortByKey(obj));
expect(sortByKey(obj)).toEqual({"0": 'b', "1": 'a', "2": 'c'})
})
})
\ No newline at end of file
......@@ -55,6 +55,7 @@ export interface ViewResult {
[key: string]: any
}
// sorted object by key (number or fisrt character)
export function sortByKey(object: { [key: string]: any }): {
[key: string]: any;
} {
......@@ -89,6 +90,7 @@ export class NpmRegistry {
this.resetIndex();
}
//
updatedProps(props?: Partial<NpmRegistryProps>): void {
const oldRegistry = this.props.registry;
Object.assign(this.props, props);
......
......@@ -5,10 +5,21 @@
"description": "NPM scripts for CloudCode packages.",
"bin": {
"run": "cloudcode-run.js",
"theiaext": "cloudcode-ext.js",
"cloudext": "cloudcode-ext.js",
"ts-clean": "cloudcode-ts-clean.js"
},
"cloudcode-main-scripts": {
"ext:clean": "cloudext compile:clean && cloudext lint:clean && cloudext test:clean",
"ext:build": "concurrently -n compile,lint -c blue,green \"cloudext compile\" \"cloudext lint\"",
"ext:compile": "ts-clean && tsc -b",
"ext:compile:fast": "tsc -p",
"ext:compile:clean": "rimraf lib *.tsbuildinfo",
"ext:lint": "eslint --cache=true --no-error-on-unmatched-pattern=true \"{src,test}/**/*.{ts,tsx}\"",
"ext:lint:clean": "rimraf .eslintcache",
"ext:watch": "concurrently --kill-others -n cleanup,tsc -c magenta,red \"ts-clean -w\" \"tsc -b -w --preserveWatchOutput\"",
"ext:watch:fast": "tsc -p -w",
"ext:test": "nyc mocha --opts ../../configs/mocha.opts \"./lib/**/*.*spec.js\"",
"ext:test:watch": "mocha -w --opts ../../configs/mocha.opts \"./lib/**/*.*spec.js\"",
"ext:test:clean": "rimraf .nyc_output coverage"
}
}
\ No newline at end of file
{
"lerna": "2.4.0",
"version": "1.0.0",
"useWorkspaces": true,
"npmClient": "yarn",
"command": {
"run": {
"stream": true
}
}
}
\ No newline at end of file
......@@ -51,6 +51,7 @@
"yargs": "^15.3.1",
"@types/jest": "^27.4.1",
"jest": "^27.5.1",
"ts-node": "^9.1.1",
"ts-jest": "^27.1.4"
},
"workspaces": [
......
......@@ -17,7 +17,6 @@
"@phosphor/virtualdom": "1",
"@phosphor/widgets": "1",
"@primer/octicons-react": "^9.0.0",
"@theia/application-package": "1.24.0",
"@types/body-parser": "^1.16.4",
"@types/cookie": "^0.3.3",
"@types/dompurify": "^2.2.2",
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册