Merge branch 'release'
This commit is contained in:
commit
f9aa003f1a
|
|
@ -54,9 +54,23 @@ class LocaleBuilder {
|
||||||
const statements = [
|
const statements = [
|
||||||
factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([factory.createImportSpecifier(false, factory.createIdentifier("CreateOperationData"), factory.createIdentifier("I18n"))])), factory.createStringLiteral("../oak-app-domain/I18n/Schema"), undefined)
|
factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([factory.createImportSpecifier(false, factory.createIdentifier("CreateOperationData"), factory.createIdentifier("I18n"))])), factory.createStringLiteral("../oak-app-domain/I18n/Schema"), undefined)
|
||||||
];
|
];
|
||||||
if (this.dependencies) {
|
// 改为在初始化时合并
|
||||||
this.dependencies.forEach((ele, idx) => statements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, factory.createIdentifier(`i18ns${idx}`), undefined), factory.createStringLiteral(`${ele}/lib/data/i18n`), undefined)));
|
/* if (this.dependencies) {
|
||||||
}
|
this.dependencies.forEach(
|
||||||
|
(ele, idx) => statements.push(
|
||||||
|
factory.createImportDeclaration(
|
||||||
|
undefined,
|
||||||
|
factory.createImportClause(
|
||||||
|
false,
|
||||||
|
factory.createIdentifier(`i18ns${idx}`),
|
||||||
|
undefined
|
||||||
|
),
|
||||||
|
factory.createStringLiteral(`${ele}/lib/data/i18n`),
|
||||||
|
undefined
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} */
|
||||||
statements.push(factory.createVariableStatement(undefined, factory.createVariableDeclarationList([factory.createVariableDeclaration(factory.createIdentifier("i18ns"), undefined, factory.createArrayTypeNode(factory.createTypeReferenceNode(factory.createIdentifier("I18n"), undefined)), factory.createArrayLiteralExpression(Object.keys(this.locales).map((k) => {
|
statements.push(factory.createVariableStatement(undefined, factory.createVariableDeclarationList([factory.createVariableDeclaration(factory.createIdentifier("i18ns"), undefined, factory.createArrayTypeNode(factory.createTypeReferenceNode(factory.createIdentifier("I18n"), undefined)), factory.createArrayLiteralExpression(Object.keys(this.locales).map((k) => {
|
||||||
const [module, position, language, data] = this.locales[k];
|
const [module, position, language, data] = this.locales[k];
|
||||||
// 用哈希计算来保证id唯一性
|
// 用哈希计算来保证id唯一性
|
||||||
|
|
@ -73,12 +87,27 @@ class LocaleBuilder {
|
||||||
factory.createPropertyAssignment(factory.createIdentifier("data"), transferObjectToObjectLiteral(data))
|
factory.createPropertyAssignment(factory.createIdentifier("data"), transferObjectToObjectLiteral(data))
|
||||||
], true);
|
], true);
|
||||||
}), true))], ts.NodeFlags.Const)));
|
}), true))], ts.NodeFlags.Const)));
|
||||||
if (this.dependencies.length > 0) {
|
/* if (this.dependencies.length > 0) {
|
||||||
statements.push(factory.createExportAssignment(undefined, undefined, factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier("i18ns"), factory.createIdentifier("concat")), undefined, this.dependencies.map((ele, idx) => factory.createIdentifier(`i18ns${idx}`)))));
|
statements.push(
|
||||||
}
|
factory.createExportAssignment(
|
||||||
else {
|
undefined,
|
||||||
statements.push(factory.createExportAssignment(undefined, undefined, factory.createIdentifier("i18ns")));
|
undefined,
|
||||||
|
factory.createCallExpression(
|
||||||
|
factory.createPropertyAccessExpression(
|
||||||
|
factory.createIdentifier("i18ns"),
|
||||||
|
factory.createIdentifier("concat")
|
||||||
|
),
|
||||||
|
undefined,
|
||||||
|
this.dependencies.map(
|
||||||
|
(ele, idx) => factory.createIdentifier(`i18ns${idx}`)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
else { */
|
||||||
|
statements.push(factory.createExportAssignment(undefined, undefined, factory.createIdentifier("i18ns")));
|
||||||
|
/* } */
|
||||||
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
||||||
const result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(statements), ts.createSourceFile("someFileName.ts", "", ts.ScriptTarget.Latest, false, ts.ScriptKind.TS));
|
const result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(statements), ts.createSourceFile("someFileName.ts", "", ts.ScriptTarget.Latest, false, ts.ScriptKind.TS));
|
||||||
const filename = (0, path_1.join)(this.pwd, 'src', 'data', 'i18n.ts');
|
const filename = (0, path_1.join)(this.pwd, 'src', 'data', 'i18n.ts');
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
export declare function buildRouter(projectDir: string, startupDir: string, watch?: boolean): void;
|
||||||
|
|
@ -0,0 +1,257 @@
|
||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.buildRouter = void 0;
|
||||||
|
const tslib_1 = require("tslib");
|
||||||
|
const path_1 = require("path");
|
||||||
|
const fs_extra_1 = require("fs-extra");
|
||||||
|
const assert_1 = tslib_1.__importDefault(require("assert"));
|
||||||
|
const ts = tslib_1.__importStar(require("typescript"));
|
||||||
|
const node_watch_1 = tslib_1.__importDefault(require("node-watch"));
|
||||||
|
const { factory } = ts;
|
||||||
|
const NameSpaceDescDict = {};
|
||||||
|
function checkPageDir(dir, relativePath, ns, type) {
|
||||||
|
let changed = false;
|
||||||
|
const { pages } = NameSpaceDescDict[ns];
|
||||||
|
const subdirs = [];
|
||||||
|
const files = (0, fs_extra_1.readdirSync)(dir);
|
||||||
|
files.forEach((file) => {
|
||||||
|
const filepath = (0, path_1.join)(dir, file);
|
||||||
|
const stat = (0, fs_extra_1.statSync)(filepath);
|
||||||
|
if (stat.isFile() &&
|
||||||
|
['web.tsx', 'web.pc.tsx', 'render.native.tsx', 'render.ios.tsx', 'render.android.tsx', 'index.xml'].includes(file)) {
|
||||||
|
if (!pages.hasOwnProperty(dir)) {
|
||||||
|
const indexJsonFile = (0, path_1.join)(dir, 'index.json');
|
||||||
|
let oakDisablePulldownRefresh = false;
|
||||||
|
if ((0, fs_extra_1.existsSync)(indexJsonFile)) {
|
||||||
|
const { enablePullDownRefresh = true, } = require(indexJsonFile);
|
||||||
|
oakDisablePulldownRefresh =
|
||||||
|
!enablePullDownRefresh;
|
||||||
|
}
|
||||||
|
pages[dir] = {
|
||||||
|
path: relativePath.replace(/\\/g, '/'),
|
||||||
|
oakDisablePulldownRefresh,
|
||||||
|
hasNative: ['render.native.tsx', 'render.ios.tsx', 'render.android.tsx'].includes(file),
|
||||||
|
hasWeb: ['web.tsx', 'web.pc.tsx'].includes(file),
|
||||||
|
hasWechatMp: file === 'index.xml',
|
||||||
|
};
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (['render.native.tsx', 'render.ios.tsx', 'render.android.tsx'].includes(file) && type === 'native') {
|
||||||
|
if (pages[dir].hasNative === false) {
|
||||||
|
pages[dir].hasNative = true;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (['web.tsx', 'web.pc.tsx'].includes(file) && type === 'web') {
|
||||||
|
if (pages[dir].hasWeb === false) {
|
||||||
|
pages[dir].hasWeb = true;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (pages[dir].hasWechatMp === false && type === 'wechatMp') {
|
||||||
|
pages[dir].hasWechatMp = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (stat.isDirectory()) {
|
||||||
|
subdirs.push(file);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
subdirs,
|
||||||
|
changed,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function traverseNsDir(nsDir, ns, type) {
|
||||||
|
NameSpaceDescDict[ns] = {
|
||||||
|
pages: {}
|
||||||
|
};
|
||||||
|
const { pages } = NameSpaceDescDict[ns];
|
||||||
|
const traverse = (dir, relativePath) => {
|
||||||
|
const { subdirs } = checkPageDir(dir, relativePath, ns, type);
|
||||||
|
subdirs.forEach((subdir) => {
|
||||||
|
const dir2 = (0, path_1.join)(dir, subdir);
|
||||||
|
const relativePath2 = (0, path_1.join)(relativePath, subdir);
|
||||||
|
traverse(dir2, relativePath2);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
traverse(nsDir, '');
|
||||||
|
}
|
||||||
|
function traversePageDir(projectDir, type) {
|
||||||
|
const pageDir = (0, path_1.join)(projectDir, 'src', 'pages');
|
||||||
|
const namespaces = (0, fs_extra_1.readdirSync)(pageDir);
|
||||||
|
namespaces.forEach((ns) => {
|
||||||
|
const nsDir = (0, path_1.join)(pageDir, ns);
|
||||||
|
const stat = (0, fs_extra_1.statSync)(nsDir);
|
||||||
|
if (stat.isDirectory()) {
|
||||||
|
traverseNsDir(nsDir, ns, type);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function makeWebAllRouters(namespaceDir, projectDir, routerFileDir) {
|
||||||
|
const nss = (0, fs_extra_1.readdirSync)(namespaceDir);
|
||||||
|
return factory.createArrayLiteralExpression(nss.map((ns) => {
|
||||||
|
(0, assert_1.default)(NameSpaceDescDict[ns], `${ns}在pages下没有对应的目录`);
|
||||||
|
const { pages } = NameSpaceDescDict[ns];
|
||||||
|
const nsIndexJsonFile = (0, path_1.join)(namespaceDir, ns, 'index.json');
|
||||||
|
let path2 = `/${ns}`;
|
||||||
|
let notFound2 = '', first2 = '';
|
||||||
|
if ((0, fs_extra_1.existsSync)(nsIndexJsonFile)) {
|
||||||
|
const { path, notFound, first } = require(nsIndexJsonFile);
|
||||||
|
if (path) {
|
||||||
|
path2 = path.replace(/\\/g, '/');
|
||||||
|
}
|
||||||
|
if (notFound) {
|
||||||
|
notFound2 = notFound.replace(/\\/g, '/');
|
||||||
|
}
|
||||||
|
if (first) {
|
||||||
|
first2 = first.replace(/\\/g, '/');
|
||||||
|
if (first2.startsWith('/')) {
|
||||||
|
first2 = first2.slice(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const children = Object.values(pages).filter((ele) => ele.hasWeb).map(({ path, oakDisablePulldownRefresh }) => {
|
||||||
|
const properties = [
|
||||||
|
factory.createPropertyAssignment('path', factory.createStringLiteral(path)),
|
||||||
|
factory.createPropertyAssignment('namespace', factory.createStringLiteral(path2)),
|
||||||
|
factory.createPropertyAssignment('meta', factory.createObjectLiteralExpression([
|
||||||
|
factory.createPropertyAssignment('oakDisablePulldownRefresh', oakDisablePulldownRefresh ? factory.createTrue() : factory.createFalse())
|
||||||
|
])),
|
||||||
|
factory.createPropertyAssignment(factory.createIdentifier("Component"), factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier("React"), factory.createIdentifier("lazy")), undefined, [factory.createArrowFunction(undefined, undefined, [], undefined, factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), factory.createCallExpression(factory.createIdentifier('import'), undefined, [
|
||||||
|
factory.createStringLiteral((0, path_1.relative)(routerFileDir, (0, path_1.join)(projectDir, 'src', 'pages', ns, path)).replace(/\\/g, '/'))
|
||||||
|
]))]))
|
||||||
|
];
|
||||||
|
if (first2 === path) {
|
||||||
|
properties.push(factory.createPropertyAssignment('isFirst', factory.createTrue()));
|
||||||
|
}
|
||||||
|
return factory.createObjectLiteralExpression(properties, true);
|
||||||
|
});
|
||||||
|
if (notFound2) {
|
||||||
|
children.push(factory.createObjectLiteralExpression([
|
||||||
|
factory.createPropertyAssignment('path', factory.createStringLiteral('*')),
|
||||||
|
factory.createPropertyAssignment('namespace', factory.createStringLiteral(path2)),
|
||||||
|
factory.createPropertyAssignment(factory.createIdentifier("Component"), factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier("React"), factory.createIdentifier("lazy")), undefined, [factory.createArrowFunction(undefined, undefined, [], undefined, factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), factory.createCallExpression(factory.createIdentifier('import'), undefined, [
|
||||||
|
factory.createStringLiteral((0, path_1.relative)(routerFileDir, (0, path_1.join)(projectDir, 'src', 'pages', ns, notFound2)).replace(/\\/g, '/'))
|
||||||
|
]))]))
|
||||||
|
], true));
|
||||||
|
}
|
||||||
|
return factory.createObjectLiteralExpression([
|
||||||
|
factory.createPropertyAssignment('path', factory.createStringLiteral(path2)),
|
||||||
|
factory.createPropertyAssignment('namespace', factory.createStringLiteral(path2)),
|
||||||
|
factory.createPropertyAssignment(factory.createIdentifier("Component"), factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier("React"), factory.createIdentifier("lazy")), undefined, [factory.createArrowFunction(undefined, undefined, [], undefined, factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), factory.createCallExpression(factory.createIdentifier('import'), undefined, [
|
||||||
|
factory.createStringLiteral((0, path_1.relative)(routerFileDir, (0, path_1.join)(namespaceDir, ns)).replace(/\\/g, '/'))
|
||||||
|
]))])),
|
||||||
|
factory.createPropertyAssignment('children', factory.createArrayLiteralExpression(children))
|
||||||
|
], true);
|
||||||
|
}), true);
|
||||||
|
}
|
||||||
|
function judgeUseOakRouterBuilder(statements) {
|
||||||
|
const stmt = statements[0];
|
||||||
|
return ts.isExpressionStatement(stmt) && ts.isStringLiteral(stmt.expression) && stmt.expression.text === 'use oak router builder';
|
||||||
|
}
|
||||||
|
function outputInWebAppDir(appDir) {
|
||||||
|
const routerFileName = (0, path_1.join)(appDir, 'router', 'allRouters.ts');
|
||||||
|
if ((0, fs_extra_1.existsSync)(routerFileName)) {
|
||||||
|
const program = ts.createProgram([routerFileName], {
|
||||||
|
removeComments: false,
|
||||||
|
});
|
||||||
|
const routerFile = program.getSourceFile(routerFileName);
|
||||||
|
(0, assert_1.default)(routerFile);
|
||||||
|
const namespaceDir = (0, path_1.join)(appDir, 'namespaces');
|
||||||
|
const { statements } = routerFile;
|
||||||
|
if (judgeUseOakRouterBuilder(statements)) {
|
||||||
|
statements.forEach((statement) => {
|
||||||
|
if (ts.isVariableStatement(statement)) {
|
||||||
|
const declaration = statement.declarationList.declarations.find(declaration => ts.isIdentifier(declaration.name) && declaration.name.text === 'allRouters');
|
||||||
|
if (declaration) {
|
||||||
|
Object.assign(declaration, {
|
||||||
|
initializer: makeWebAllRouters(namespaceDir, (0, path_1.join)(appDir, '../../../..'), (0, path_1.dirname)(routerFileName))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed, removeComments: false });
|
||||||
|
const result = printer.printNode(ts.EmitHint.Unspecified, routerFile, routerFile);
|
||||||
|
(0, fs_extra_1.writeFileSync)(routerFileName, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.warn(`${appDir}的目录结构未按照标准建立,缺少了${routerFileName}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function outputInWebDir(dir) {
|
||||||
|
const srcAppDir = (0, path_1.join)(dir, 'src', 'app');
|
||||||
|
const apps = (0, fs_extra_1.readdirSync)(srcAppDir);
|
||||||
|
apps.forEach((app) => {
|
||||||
|
const appDir = (0, path_1.join)(srcAppDir, app);
|
||||||
|
const stat = (0, fs_extra_1.statSync)(appDir);
|
||||||
|
if (stat.isDirectory()) {
|
||||||
|
outputInWebAppDir(appDir);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function watchDir(projectDir, startupDir, type) {
|
||||||
|
const srcPageDir = (0, path_1.join)(projectDir, 'src', 'pages');
|
||||||
|
console.log('watch dir ', srcPageDir);
|
||||||
|
if (startupDir.startsWith('web')) {
|
||||||
|
const srcAppDir = (0, path_1.join)(projectDir, startupDir, 'src', 'app');
|
||||||
|
const apps = (0, fs_extra_1.readdirSync)(srcAppDir);
|
||||||
|
const tryOutputAppDir = (ns) => {
|
||||||
|
apps.forEach((app) => {
|
||||||
|
const appDir = (0, path_1.join)(srcAppDir, app);
|
||||||
|
const namespaceDir = (0, path_1.join)(appDir, 'namespaces');
|
||||||
|
const namespaces = (0, fs_extra_1.readdirSync)(namespaceDir);
|
||||||
|
if (namespaces.includes(ns)) {
|
||||||
|
outputInWebAppDir(appDir);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
(0, node_watch_1.default)(srcPageDir, {
|
||||||
|
recursive: true,
|
||||||
|
filter: new RegExp('web\.tsx|web\.pc\.tsx|index\.xml|render\.(native|ios|android)\.tsx'),
|
||||||
|
}, (evt, filepath) => {
|
||||||
|
const dir = (0, path_1.dirname)(filepath);
|
||||||
|
const relativeDir = (0, path_1.relative)((0, path_1.join)(projectDir, 'src', 'pages'), filepath);
|
||||||
|
const ns = relativeDir.split('\\')[0];
|
||||||
|
const relativePath = (0, path_1.relative)(ns, (0, path_1.dirname)(relativeDir));
|
||||||
|
const { pages } = NameSpaceDescDict[ns];
|
||||||
|
console.log(filepath, dir, ns);
|
||||||
|
if (evt === 'remove') {
|
||||||
|
if ((0, fs_extra_1.existsSync)(dir)) {
|
||||||
|
const { changed } = checkPageDir(dir, relativePath, ns, type);
|
||||||
|
if (changed) {
|
||||||
|
tryOutputAppDir(ns);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
delete pages[dir];
|
||||||
|
tryOutputAppDir(ns);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const { changed } = checkPageDir(dir, relativePath, ns, type);
|
||||||
|
if (changed) {
|
||||||
|
tryOutputAppDir(ns);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function buildRouter(projectDir, startupDir, watch) {
|
||||||
|
const type = startupDir.startsWith('web') ? 'web' : (startupDir.startsWith('native') ? 'native' : 'wechatMp');
|
||||||
|
traversePageDir(projectDir, type);
|
||||||
|
const subDir = (0, fs_extra_1.readdirSync)(projectDir);
|
||||||
|
(0, assert_1.default)(subDir.includes(startupDir));
|
||||||
|
if (startupDir.startsWith('web')) {
|
||||||
|
outputInWebDir((0, path_1.join)(projectDir, startupDir));
|
||||||
|
}
|
||||||
|
// todo native
|
||||||
|
if (watch) {
|
||||||
|
watchDir(projectDir, startupDir, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.buildRouter = buildRouter;
|
||||||
|
|
@ -18,9 +18,10 @@ export declare abstract class CascadeStore<ED extends EntityDict & BaseEntityDic
|
||||||
registerOperationRewriter(rewriter: OperationRewriter<ED, AsyncContext<ED> | SyncContext<ED>, OperateOption>): void;
|
registerOperationRewriter(rewriter: OperationRewriter<ED, AsyncContext<ED> | SyncContext<ED>, OperateOption>): void;
|
||||||
registerSelectionRewriter(rewriter: SelectionRewriter<ED, AsyncContext<ED> | SyncContext<ED>, SelectOption>): void;
|
registerSelectionRewriter(rewriter: SelectionRewriter<ED, AsyncContext<ED> | SyncContext<ED>, SelectOption>): void;
|
||||||
protected abstract selectAbjointRow<T extends keyof ED, OP extends SelectOption, Cxt extends SyncContext<ED>>(entity: T, selection: ED[T]['Selection'], context: Cxt, option: OP): Partial<ED[T]['Schema']>[];
|
protected abstract selectAbjointRow<T extends keyof ED, OP extends SelectOption, Cxt extends SyncContext<ED>>(entity: T, selection: ED[T]['Selection'], context: Cxt, option: OP): Partial<ED[T]['Schema']>[];
|
||||||
|
protected abstract countAbjointRow<T extends keyof ED, OP extends SelectOption, Cxt extends SyncContext<ED>>(entity: T, selection: Pick<ED[T]['Selection'], 'filter' | 'count'>, context: Cxt, option: OP): number;
|
||||||
|
protected abstract countAbjointRowAsync<T extends keyof ED, OP extends SelectOption, Cxt extends AsyncContext<ED>>(entity: T, selection: Pick<ED[T]['Selection'], 'filter' | 'count'>, context: Cxt, option: OP): Promise<number>;
|
||||||
protected abstract updateAbjointRow<T extends keyof ED, OP extends OperateOption, Cxt extends SyncContext<ED>>(entity: T, operation: ED[T]['Operation'], context: Cxt, option: OP): number;
|
protected abstract updateAbjointRow<T extends keyof ED, OP extends OperateOption, Cxt extends SyncContext<ED>>(entity: T, operation: ED[T]['Operation'], context: Cxt, option: OP): number;
|
||||||
protected abstract selectAbjointRowAsync<T extends keyof ED, OP extends SelectOption, Cxt extends AsyncContext<ED>>(entity: T, selection: ED[T]['Selection'], context: Cxt, option: OP): Promise<Partial<ED[T]['Schema']>[]>;
|
protected abstract selectAbjointRowAsync<T extends keyof ED, OP extends SelectOption, Cxt extends AsyncContext<ED>>(entity: T, selection: ED[T]['Selection'], context: Cxt, option: OP): Promise<Partial<ED[T]['Schema']>[]>;
|
||||||
protected abstract countAsync<T extends keyof ED, OP extends SelectOption, Cxt extends AsyncContext<ED>>(entity: T, selection: Pick<ED[T]['Selection'], 'filter' | 'count'>, context: Cxt, option: OP): Promise<number>;
|
|
||||||
protected abstract updateAbjointRowAsync<T extends keyof ED, OP extends OperateOption, Cxt extends AsyncContext<ED>>(entity: T, operation: ED[T]['Create'] | ED[T]['Update'] | ED[T]['Remove'], context: Cxt, option: OP): Promise<number>;
|
protected abstract updateAbjointRowAsync<T extends keyof ED, OP extends OperateOption, Cxt extends AsyncContext<ED>>(entity: T, operation: ED[T]['Create'] | ED[T]['Update'] | ED[T]['Remove'], context: Cxt, option: OP): Promise<number>;
|
||||||
protected abstract aggregateAbjointRowSync<T extends keyof ED, OP extends SelectOption, Cxt extends SyncContext<ED>>(entity: T, aggregation: ED[T]['Aggregation'], context: Cxt, option: OP): AggregationResult<ED[T]['Schema']>;
|
protected abstract aggregateAbjointRowSync<T extends keyof ED, OP extends SelectOption, Cxt extends SyncContext<ED>>(entity: T, aggregation: ED[T]['Aggregation'], context: Cxt, option: OP): AggregationResult<ED[T]['Schema']>;
|
||||||
protected abstract aggregateAbjointRowAsync<T extends keyof ED, OP extends SelectOption, Cxt extends AsyncContext<ED>>(entity: T, aggregation: ED[T]['Aggregation'], context: Cxt, option: OP): Promise<AggregationResult<ED[T]['Schema']>>;
|
protected abstract aggregateAbjointRowAsync<T extends keyof ED, OP extends SelectOption, Cxt extends AsyncContext<ED>>(entity: T, aggregation: ED[T]['Aggregation'], context: Cxt, option: OP): Promise<AggregationResult<ED[T]['Schema']>>;
|
||||||
|
|
@ -103,4 +104,6 @@ export declare abstract class CascadeStore<ED extends EntityDict & BaseEntityDic
|
||||||
protected selectSync<T extends keyof ED, OP extends SelectOption, Cxt extends SyncContext<ED>>(entity: T, selection: ED[T]['Selection'], context: Cxt, option: OP): Partial<ED[T]['Schema']>[];
|
protected selectSync<T extends keyof ED, OP extends SelectOption, Cxt extends SyncContext<ED>>(entity: T, selection: ED[T]['Selection'], context: Cxt, option: OP): Partial<ED[T]['Schema']>[];
|
||||||
protected operateSync<T extends keyof ED, Cxt extends SyncContext<ED>, OP extends OperateOption>(entity: T, operation: ED[T]['Operation'], context: Cxt, option: OP): OperationResult<ED>;
|
protected operateSync<T extends keyof ED, Cxt extends SyncContext<ED>, OP extends OperateOption>(entity: T, operation: ED[T]['Operation'], context: Cxt, option: OP): OperationResult<ED>;
|
||||||
protected operateAsync<T extends keyof ED, Cxt extends AsyncContext<ED>, OP extends OperateOption>(entity: T, operation: ED[T]['Operation'], context: Cxt, option: OP): Promise<OperationResult<ED>>;
|
protected operateAsync<T extends keyof ED, Cxt extends AsyncContext<ED>, OP extends OperateOption>(entity: T, operation: ED[T]['Operation'], context: Cxt, option: OP): Promise<OperationResult<ED>>;
|
||||||
|
protected countSync<T extends keyof ED, OP extends SelectOption, Cxt extends SyncContext<ED>>(entity: T, selection: Pick<ED[T]['Selection'], 'filter' | 'count'>, context: Cxt, option: OP): number;
|
||||||
|
protected countAsync<T extends keyof ED, OP extends SelectOption, Cxt extends AsyncContext<ED>>(entity: T, selection: Pick<ED[T]['Selection'], 'filter' | 'count'>, context: Cxt, option: OP): Promise<number>;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ class CascadeStore extends RowStore_1.RowStore {
|
||||||
this.reinforceSelectionInner(entity, selection, context);
|
this.reinforceSelectionInner(entity, selection, context);
|
||||||
}
|
}
|
||||||
this.selectionRewriters.forEach(ele => {
|
this.selectionRewriters.forEach(ele => {
|
||||||
const result = ele(this.getSchema(), entity, selection, context, option);
|
const result = ele(this.getSchema(), entity, selection, context, option, isAggr);
|
||||||
(0, assert_1.default)(!(result instanceof Promise));
|
(0, assert_1.default)(!(result instanceof Promise));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -1736,5 +1736,13 @@ class CascadeStore extends RowStore_1.RowStore {
|
||||||
await this.reinforceOperation(entity, operation, context, option);
|
await this.reinforceOperation(entity, operation, context, option);
|
||||||
return this.cascadeUpdateAsync(entity, operation, context, option);
|
return this.cascadeUpdateAsync(entity, operation, context, option);
|
||||||
}
|
}
|
||||||
|
countSync(entity, selection, context, option) {
|
||||||
|
this.reinforceSelectionSync(entity, selection, context, option, true); // 这样写可能有问题的,虽然能跳过本地的projection补全,但如果有更多的selectionRewriter注入可能会出问题。by Xc 20231220
|
||||||
|
return this.countAbjointRow(entity, selection, context, option);
|
||||||
|
}
|
||||||
|
countAsync(entity, selection, context, option) {
|
||||||
|
this.reinforceSelectionAsync(entity, selection, context, option, true); // 这样写可能有问题的,虽然能跳过本地的projection补全,但如果有更多的selectionRewriter注入可能会出问题。by Xc 20231220
|
||||||
|
return this.countAbjointRowAsync(entity, selection, context, option);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
exports.CascadeStore = CascadeStore;
|
exports.CascadeStore = CascadeStore;
|
||||||
|
|
|
||||||
|
|
@ -452,7 +452,7 @@ class TriggerExecutor {
|
||||||
const rs = grouped[uuid];
|
const rs = grouped[uuid];
|
||||||
const { [Entity_1.TriggerDataAttribute]: triggerData } = rs[0];
|
const { [Entity_1.TriggerDataAttribute]: triggerData } = rs[0];
|
||||||
const { name, cxtStr, option } = triggerData;
|
const { name, cxtStr, option } = triggerData;
|
||||||
await context.initialize(JSON.parse(cxtStr));
|
// await context.initialize(JSON.parse(cxtStr)); // 这里token有可能过期(用户注销),先用root态模拟吧
|
||||||
await this.execVolatileTrigger(entity, name, rs.map(ele => ele.id), context, option);
|
await this.execVolatileTrigger(entity, name, rs.map(ele => ele.id), context, option);
|
||||||
}
|
}
|
||||||
await context.commit();
|
await context.commit();
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@ function makeIntrinsicCTWs(schema, actionDefDict) {
|
||||||
action: 'create',
|
action: 'create',
|
||||||
type: 'data',
|
type: 'data',
|
||||||
entity,
|
entity,
|
||||||
priority: 10,
|
priority: 10, // 优先级要高,先于真正的data检查进行
|
||||||
checker: (data) => {
|
checker: (data) => {
|
||||||
if (data instanceof Array) {
|
if (data instanceof Array) {
|
||||||
data.forEach(ele => {
|
data.forEach(ele => {
|
||||||
|
|
@ -181,7 +181,7 @@ function makeIntrinsicCTWs(schema, actionDefDict) {
|
||||||
entity,
|
entity,
|
||||||
action: 'create',
|
action: 'create',
|
||||||
type: 'logicalData',
|
type: 'logicalData',
|
||||||
priority: types_1.CHECKER_MAX_PRIORITY,
|
priority: types_1.CHECKER_MAX_PRIORITY, // 优先级要放在最低,所有前置的checker/trigger将数据完整之后再在这里检测
|
||||||
checker: (operation, context) => {
|
checker: (operation, context) => {
|
||||||
const { data } = operation;
|
const { data } = operation;
|
||||||
if (data instanceof Array) {
|
if (data instanceof Array) {
|
||||||
|
|
@ -197,9 +197,9 @@ function makeIntrinsicCTWs(schema, actionDefDict) {
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
entity,
|
entity,
|
||||||
action: 'update',
|
action: 'update', // 只检查update,其它状态转换的action应该不会涉及unique约束的属性
|
||||||
type: 'logicalData',
|
type: 'logicalData',
|
||||||
priority: types_1.CHECKER_MAX_PRIORITY,
|
priority: types_1.CHECKER_MAX_PRIORITY, // 优先级要放在最低,所有前置的checker/trigger将数据完整之后再在这里检测
|
||||||
checker: (operation, context) => {
|
checker: (operation, context) => {
|
||||||
const { data, filter: operationFilter } = operation;
|
const { data, filter: operationFilter } = operation;
|
||||||
if (data) {
|
if (data) {
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ class SimpleConnector {
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
// fetch返回异常一定是网络异常
|
// fetch返回异常一定是网络异常
|
||||||
throw new types_1.OakNetworkException();
|
throw new types_1.OakNetworkException(`请求[${this.serverAspectUrl}],发生网络异常`);
|
||||||
}
|
}
|
||||||
if (response.status > 299) {
|
if (response.status > 299) {
|
||||||
const err = new types_1.OakServerProxyException(`网络请求返回status是${response.status}`);
|
const err = new types_1.OakServerProxyException(`网络请求返回status是${response.status}`);
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
/// <reference types="node" />
|
||||||
/**
|
/**
|
||||||
* 防止assert打包体积过大,从这里引用
|
* 防止assert打包体积过大,从这里引用
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "oak-domain",
|
"name": "oak-domain",
|
||||||
"version": "4.0.1",
|
"version": "4.0.2",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "XuChang"
|
"name": "XuChang"
|
||||||
},
|
},
|
||||||
|
|
@ -37,6 +37,7 @@
|
||||||
"fs-extra": "^10.0.0",
|
"fs-extra": "^10.0.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mocha": "^8.2.1",
|
"mocha": "^8.2.1",
|
||||||
|
"node-watch": "^0.7.4",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"tslib": "^2.4.0",
|
"tslib": "^2.4.0",
|
||||||
"typescript": "^5.2.2"
|
"typescript": "^5.2.2"
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,8 @@ export default class LocaleBuilder {
|
||||||
)
|
)
|
||||||
];
|
];
|
||||||
|
|
||||||
if (this.dependencies) {
|
// 改为在初始化时合并
|
||||||
|
/* if (this.dependencies) {
|
||||||
this.dependencies.forEach(
|
this.dependencies.forEach(
|
||||||
(ele, idx) => statements.push(
|
(ele, idx) => statements.push(
|
||||||
factory.createImportDeclaration(
|
factory.createImportDeclaration(
|
||||||
|
|
@ -95,7 +96,7 @@ export default class LocaleBuilder {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
} */
|
||||||
|
|
||||||
statements.push(
|
statements.push(
|
||||||
factory.createVariableStatement(
|
factory.createVariableStatement(
|
||||||
|
|
@ -159,7 +160,7 @@ export default class LocaleBuilder {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this.dependencies.length > 0) {
|
/* if (this.dependencies.length > 0) {
|
||||||
statements.push(
|
statements.push(
|
||||||
factory.createExportAssignment(
|
factory.createExportAssignment(
|
||||||
undefined,
|
undefined,
|
||||||
|
|
@ -177,7 +178,7 @@ export default class LocaleBuilder {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
else { */
|
||||||
statements.push(
|
statements.push(
|
||||||
factory.createExportAssignment(
|
factory.createExportAssignment(
|
||||||
undefined,
|
undefined,
|
||||||
|
|
@ -185,7 +186,7 @@ export default class LocaleBuilder {
|
||||||
factory.createIdentifier("i18ns")
|
factory.createIdentifier("i18ns")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
/* } */
|
||||||
|
|
||||||
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
||||||
const result = printer.printList(
|
const result = printer.printList(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,469 @@
|
||||||
|
import { join, relative, dirname } from 'path';
|
||||||
|
import { readdirSync, statSync, existsSync, writeFileSync } from 'fs-extra';
|
||||||
|
import assert from 'assert';
|
||||||
|
import * as ts from 'typescript';
|
||||||
|
import NodeWatch from 'node-watch';
|
||||||
|
const { factory } = ts;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 一个项目,根据其pages下的目录结构,构建出web/native工程下的router以及pageMap
|
||||||
|
* wechatMp暂不处理
|
||||||
|
* 项目目录结构应为:
|
||||||
|
*
|
||||||
|
* -src
|
||||||
|
* --pages
|
||||||
|
* ---${namespace1}
|
||||||
|
* -----${page1}
|
||||||
|
* -------${subPage1.1}
|
||||||
|
* -------${subPage1.2}
|
||||||
|
* -----${page2}
|
||||||
|
* ---${namespace2}
|
||||||
|
* -----${page3}
|
||||||
|
*
|
||||||
|
* -web
|
||||||
|
* --src
|
||||||
|
* ---${appName}
|
||||||
|
* -----namespaces
|
||||||
|
* -------${namespace1}
|
||||||
|
* ---------index.json (此namespace下的配置)
|
||||||
|
* ---------pageMap.json (编译器将pageMap注入到这里)
|
||||||
|
* -------${namespace2}
|
||||||
|
* -----router
|
||||||
|
* ---------index.ts (编译器将router.ts注入到这里)
|
||||||
|
*
|
||||||
|
* -native
|
||||||
|
* --namspaces
|
||||||
|
* ----${namespace1}
|
||||||
|
* -------index.json (此namespace下的配置)
|
||||||
|
* -------pageMap.json (编译器将pageMap注入到这里)
|
||||||
|
* --router
|
||||||
|
* ----index.ts (编译器将router.ts注入到这里)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
type PageDesc = {
|
||||||
|
path: string;
|
||||||
|
oakDisablePulldownRefresh: boolean;
|
||||||
|
hasWeb: boolean;
|
||||||
|
hasNative: boolean;
|
||||||
|
hasWechatMp: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
type NamespaceDesc = {
|
||||||
|
pages: Record<string, PageDesc>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NameSpaceDescDict: Record<string, NamespaceDesc> = {};
|
||||||
|
|
||||||
|
function checkPageDir(dir: string, relativePath: string, ns: string, type: 'native' | 'web' | 'wechatMp') {
|
||||||
|
let changed = false;
|
||||||
|
const { pages } = NameSpaceDescDict[ns];
|
||||||
|
const subdirs: string[] = [];
|
||||||
|
const files = readdirSync(dir);
|
||||||
|
files.forEach((file) => {
|
||||||
|
const filepath = join(dir, file);
|
||||||
|
const stat = statSync(filepath);
|
||||||
|
if (stat.isFile() &&
|
||||||
|
['web.tsx', 'web.pc.tsx', 'render.native.tsx', 'render.ios.tsx', 'render.android.tsx', 'index.xml'].includes(
|
||||||
|
file
|
||||||
|
)) {
|
||||||
|
if (!pages.hasOwnProperty(dir)) {
|
||||||
|
const indexJsonFile = join(dir, 'index.json');
|
||||||
|
let oakDisablePulldownRefresh = false;
|
||||||
|
if (existsSync(indexJsonFile)) {
|
||||||
|
const {
|
||||||
|
enablePullDownRefresh = true,
|
||||||
|
} = require(indexJsonFile);
|
||||||
|
oakDisablePulldownRefresh =
|
||||||
|
!enablePullDownRefresh;
|
||||||
|
}
|
||||||
|
pages[dir] = {
|
||||||
|
path: relativePath.replace(/\\/g, '/'),
|
||||||
|
oakDisablePulldownRefresh,
|
||||||
|
hasNative: ['render.native.tsx', 'render.ios.tsx', 'render.android.tsx'].includes(file),
|
||||||
|
hasWeb: ['web.tsx', 'web.pc.tsx'].includes(file),
|
||||||
|
hasWechatMp: file === 'index.xml',
|
||||||
|
};
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (['render.native.tsx', 'render.ios.tsx', 'render.android.tsx'].includes(file) && type === 'native') {
|
||||||
|
if (pages[dir].hasNative === false) {
|
||||||
|
pages[dir].hasNative = true;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (['web.tsx', 'web.pc.tsx'].includes(file) && type === 'web') {
|
||||||
|
if (pages[dir].hasWeb === false) {
|
||||||
|
pages[dir].hasWeb = true;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (pages[dir].hasWechatMp === false && type === 'wechatMp') {
|
||||||
|
pages[dir].hasWechatMp = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (stat.isDirectory()) {
|
||||||
|
subdirs.push(file);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
subdirs,
|
||||||
|
changed,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function traverseNsDir(nsDir: string, ns: string, type: 'native' | 'web' | 'wechatMp') {
|
||||||
|
NameSpaceDescDict[ns] = {
|
||||||
|
pages: {}
|
||||||
|
};
|
||||||
|
const { pages } = NameSpaceDescDict[ns];
|
||||||
|
const traverse = (dir: string, relativePath: string) => {
|
||||||
|
const { subdirs } = checkPageDir(dir, relativePath, ns, type);
|
||||||
|
subdirs.forEach(
|
||||||
|
(subdir) => {
|
||||||
|
const dir2 = join(dir, subdir);
|
||||||
|
const relativePath2 = join(relativePath, subdir);
|
||||||
|
traverse(dir2, relativePath2);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
traverse(nsDir, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function traversePageDir(projectDir: string, type: 'native' | 'web' | 'wechatMp') {
|
||||||
|
const pageDir = join(projectDir, 'src', 'pages');
|
||||||
|
|
||||||
|
const namespaces = readdirSync(pageDir);
|
||||||
|
namespaces.forEach(
|
||||||
|
(ns) => {
|
||||||
|
const nsDir = join(pageDir, ns);
|
||||||
|
const stat = statSync(nsDir);
|
||||||
|
if (stat.isDirectory()) {
|
||||||
|
traverseNsDir(nsDir, ns, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeWebAllRouters(namespaceDir: string, projectDir: string, routerFileDir: string) {
|
||||||
|
const nss = readdirSync(namespaceDir);
|
||||||
|
|
||||||
|
return factory.createArrayLiteralExpression(
|
||||||
|
nss.map(
|
||||||
|
(ns) => {
|
||||||
|
assert(NameSpaceDescDict[ns], `${ns}在pages下没有对应的目录`);
|
||||||
|
const { pages } = NameSpaceDescDict[ns];
|
||||||
|
const nsIndexJsonFile = join(namespaceDir, ns, 'index.json');
|
||||||
|
let path2 = `/${ns}`;
|
||||||
|
let notFound2 = '', first2 = '';
|
||||||
|
if (existsSync(nsIndexJsonFile)) {
|
||||||
|
const { path, notFound, first } = require(nsIndexJsonFile);
|
||||||
|
if (path) {
|
||||||
|
path2 = path.replace(/\\/g, '/');
|
||||||
|
}
|
||||||
|
if (notFound) {
|
||||||
|
notFound2 = notFound.replace(/\\/g, '/');
|
||||||
|
}
|
||||||
|
if (first) {
|
||||||
|
first2 = first.replace(/\\/g, '/');
|
||||||
|
if (first2.startsWith('/')) {
|
||||||
|
first2 = first2.slice(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const children = Object.values(pages).filter(
|
||||||
|
(ele) => ele.hasWeb
|
||||||
|
).map(
|
||||||
|
({ path, oakDisablePulldownRefresh }) => {
|
||||||
|
const properties = [
|
||||||
|
factory.createPropertyAssignment(
|
||||||
|
'path',
|
||||||
|
factory.createStringLiteral(path)
|
||||||
|
),
|
||||||
|
factory.createPropertyAssignment(
|
||||||
|
'namespace',
|
||||||
|
factory.createStringLiteral(path2)
|
||||||
|
),
|
||||||
|
factory.createPropertyAssignment(
|
||||||
|
'meta',
|
||||||
|
factory.createObjectLiteralExpression(
|
||||||
|
[
|
||||||
|
factory.createPropertyAssignment(
|
||||||
|
'oakDisablePulldownRefresh',
|
||||||
|
oakDisablePulldownRefresh ? factory.createTrue() : factory.createFalse()
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
factory.createPropertyAssignment(
|
||||||
|
factory.createIdentifier("Component"),
|
||||||
|
factory.createCallExpression(
|
||||||
|
factory.createPropertyAccessExpression(
|
||||||
|
factory.createIdentifier("React"),
|
||||||
|
factory.createIdentifier("lazy")
|
||||||
|
),
|
||||||
|
undefined,
|
||||||
|
[factory.createArrowFunction(
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
[],
|
||||||
|
undefined,
|
||||||
|
factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
|
||||||
|
factory.createCallExpression(
|
||||||
|
factory.createIdentifier('import'),
|
||||||
|
undefined,
|
||||||
|
[
|
||||||
|
factory.createStringLiteral(
|
||||||
|
relative(routerFileDir, join(projectDir, 'src', 'pages', ns, path)).replace(/\\/g, '/')
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
];
|
||||||
|
if (first2 === path) {
|
||||||
|
properties.push(
|
||||||
|
factory.createPropertyAssignment(
|
||||||
|
'isFirst',
|
||||||
|
factory.createTrue()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return factory.createObjectLiteralExpression(
|
||||||
|
properties,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (notFound2) {
|
||||||
|
children.push(
|
||||||
|
factory.createObjectLiteralExpression(
|
||||||
|
[
|
||||||
|
factory.createPropertyAssignment(
|
||||||
|
'path',
|
||||||
|
factory.createStringLiteral('*')
|
||||||
|
),
|
||||||
|
factory.createPropertyAssignment(
|
||||||
|
'namespace',
|
||||||
|
factory.createStringLiteral(path2)
|
||||||
|
),
|
||||||
|
factory.createPropertyAssignment(
|
||||||
|
factory.createIdentifier("Component"),
|
||||||
|
factory.createCallExpression(
|
||||||
|
factory.createPropertyAccessExpression(
|
||||||
|
factory.createIdentifier("React"),
|
||||||
|
factory.createIdentifier("lazy")
|
||||||
|
),
|
||||||
|
undefined,
|
||||||
|
[factory.createArrowFunction(
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
[],
|
||||||
|
undefined,
|
||||||
|
factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
|
||||||
|
factory.createCallExpression(
|
||||||
|
factory.createIdentifier('import'),
|
||||||
|
undefined,
|
||||||
|
[
|
||||||
|
factory.createStringLiteral(
|
||||||
|
relative(routerFileDir, join(projectDir, 'src', 'pages', ns, notFound2)).replace(/\\/g, '/')
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
],
|
||||||
|
true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return factory.createObjectLiteralExpression(
|
||||||
|
[
|
||||||
|
factory.createPropertyAssignment(
|
||||||
|
'path',
|
||||||
|
factory.createStringLiteral(path2)
|
||||||
|
),
|
||||||
|
factory.createPropertyAssignment(
|
||||||
|
'namespace',
|
||||||
|
factory.createStringLiteral(path2)
|
||||||
|
),
|
||||||
|
factory.createPropertyAssignment(
|
||||||
|
factory.createIdentifier("Component"),
|
||||||
|
factory.createCallExpression(
|
||||||
|
factory.createPropertyAccessExpression(
|
||||||
|
factory.createIdentifier("React"),
|
||||||
|
factory.createIdentifier("lazy")
|
||||||
|
),
|
||||||
|
undefined,
|
||||||
|
[factory.createArrowFunction(
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
[],
|
||||||
|
undefined,
|
||||||
|
factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
|
||||||
|
factory.createCallExpression(
|
||||||
|
factory.createIdentifier('import'),
|
||||||
|
undefined,
|
||||||
|
[
|
||||||
|
factory.createStringLiteral(
|
||||||
|
relative(routerFileDir, join(namespaceDir, ns)).replace(/\\/g, '/')
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
factory.createPropertyAssignment(
|
||||||
|
'children',
|
||||||
|
factory.createArrayLiteralExpression(
|
||||||
|
children
|
||||||
|
)
|
||||||
|
)
|
||||||
|
],
|
||||||
|
true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function judgeUseOakRouterBuilder(statements: ts.NodeArray<ts.Statement>) {
|
||||||
|
const stmt = statements[0];
|
||||||
|
return ts.isExpressionStatement(stmt) && ts.isStringLiteral(stmt.expression) && stmt.expression.text === 'use oak router builder';
|
||||||
|
}
|
||||||
|
|
||||||
|
function outputInWebAppDir(appDir: string) {
|
||||||
|
const routerFileName = join(appDir, 'router', 'allRouters.ts');
|
||||||
|
if (existsSync(routerFileName)) {
|
||||||
|
const program = ts.createProgram([routerFileName], {
|
||||||
|
removeComments: false,
|
||||||
|
});
|
||||||
|
const routerFile = program.getSourceFile(routerFileName);
|
||||||
|
assert(routerFile);
|
||||||
|
const namespaceDir = join(appDir, 'namespaces');
|
||||||
|
const { statements } = routerFile;
|
||||||
|
if (judgeUseOakRouterBuilder(statements)) {
|
||||||
|
statements.forEach(
|
||||||
|
(statement) => {
|
||||||
|
if (ts.isVariableStatement(statement)) {
|
||||||
|
const declaration = statement.declarationList.declarations.find(
|
||||||
|
declaration => ts.isIdentifier(declaration.name) && declaration.name.text === 'allRouters'
|
||||||
|
);
|
||||||
|
if (declaration) {
|
||||||
|
Object.assign(declaration, {
|
||||||
|
initializer: makeWebAllRouters(namespaceDir, join(appDir, '../../../..'), dirname(routerFileName))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed, removeComments: false });
|
||||||
|
const result = printer.printNode(
|
||||||
|
ts.EmitHint.Unspecified,
|
||||||
|
routerFile,
|
||||||
|
routerFile,
|
||||||
|
);
|
||||||
|
|
||||||
|
writeFileSync(routerFileName, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.warn(`${appDir}的目录结构未按照标准建立,缺少了${routerFileName}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function outputInWebDir(dir: string) {
|
||||||
|
const srcAppDir = join(dir, 'src', 'app');
|
||||||
|
const apps = readdirSync(srcAppDir);
|
||||||
|
apps.forEach(
|
||||||
|
(app) => {
|
||||||
|
const appDir = join(srcAppDir, app);
|
||||||
|
const stat = statSync(appDir);
|
||||||
|
|
||||||
|
if (stat.isDirectory()) {
|
||||||
|
outputInWebAppDir(appDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function watchDir(projectDir: string, startupDir: string, type: 'native' | 'web' | 'wechatMp') {
|
||||||
|
const srcPageDir = join(projectDir, 'src', 'pages');
|
||||||
|
console.log('watch dir ', srcPageDir);
|
||||||
|
|
||||||
|
if (startupDir.startsWith('web')) {
|
||||||
|
const srcAppDir = join(projectDir, startupDir, 'src', 'app');
|
||||||
|
const apps = readdirSync(srcAppDir);
|
||||||
|
const tryOutputAppDir = (ns: string) => {
|
||||||
|
apps.forEach(
|
||||||
|
(app) => {
|
||||||
|
const appDir = join(srcAppDir, app);
|
||||||
|
const namespaceDir = join(appDir, 'namespaces');
|
||||||
|
const namespaces = readdirSync(namespaceDir);
|
||||||
|
if (namespaces.includes(ns)) {
|
||||||
|
outputInWebAppDir(appDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeWatch(srcPageDir, {
|
||||||
|
recursive: true,
|
||||||
|
filter: new RegExp('web\.tsx|web\.pc\.tsx|index\.xml|render\.(native|ios|android)\.tsx'),
|
||||||
|
}, (evt, filepath) => {
|
||||||
|
const dir = dirname(filepath);
|
||||||
|
const relativeDir = relative(join(projectDir, 'src', 'pages'), filepath);
|
||||||
|
const ns = relativeDir.split('\\')[0];
|
||||||
|
const relativePath = relative(ns, dirname(relativeDir));
|
||||||
|
const { pages } = NameSpaceDescDict[ns];
|
||||||
|
console.log(filepath, dir, ns);
|
||||||
|
if (evt === 'remove') {
|
||||||
|
if (existsSync(dir)) {
|
||||||
|
const { changed } = checkPageDir(dir, relativePath, ns, type);
|
||||||
|
if (changed) {
|
||||||
|
tryOutputAppDir(ns);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
delete pages[dir];
|
||||||
|
tryOutputAppDir(ns);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const { changed } = checkPageDir(dir, relativePath, ns, type);
|
||||||
|
if (changed) {
|
||||||
|
tryOutputAppDir(ns);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildRouter(projectDir: string, startupDir: string, watch?: boolean) {
|
||||||
|
const type = startupDir.startsWith('web') ? 'web' : (startupDir.startsWith('native') ? 'native' : 'wechatMp');
|
||||||
|
traversePageDir(projectDir, type);
|
||||||
|
|
||||||
|
const subDir = readdirSync(projectDir);
|
||||||
|
assert(subDir.includes(startupDir));
|
||||||
|
|
||||||
|
if (startupDir.startsWith('web')) {
|
||||||
|
outputInWebDir(join(projectDir, startupDir));
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo native
|
||||||
|
|
||||||
|
if (watch) {
|
||||||
|
watchDir(projectDir, startupDir, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -62,7 +62,7 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
|
||||||
|
|
||||||
this.selectionRewriters.forEach(
|
this.selectionRewriters.forEach(
|
||||||
ele => {
|
ele => {
|
||||||
const result = ele(this.getSchema(), entity, selection, context, option);
|
const result = ele(this.getSchema(), entity, selection, context, option, isAggr);
|
||||||
assert(!(result instanceof Promise));
|
assert(!(result instanceof Promise));
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
@ -377,7 +377,6 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
|
||||||
$$createAt$$: 1,
|
$$createAt$$: 1,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async reinforceOperation<Cxt extends AsyncContext<ED>, Op extends OperateOption>(
|
private async reinforceOperation<Cxt extends AsyncContext<ED>, Op extends OperateOption>(
|
||||||
|
|
@ -403,6 +402,19 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
|
||||||
selection: ED[T]['Selection'],
|
selection: ED[T]['Selection'],
|
||||||
context: Cxt,
|
context: Cxt,
|
||||||
option: OP): Partial<ED[T]['Schema']>[];
|
option: OP): Partial<ED[T]['Schema']>[];
|
||||||
|
|
||||||
|
protected abstract countAbjointRow<T extends keyof ED, OP extends SelectOption, Cxt extends SyncContext<ED>>(
|
||||||
|
entity: T,
|
||||||
|
selection: Pick<ED[T]['Selection'], 'filter' | 'count'>,
|
||||||
|
context: Cxt,
|
||||||
|
option: OP): number;
|
||||||
|
|
||||||
|
|
||||||
|
protected abstract countAbjointRowAsync<T extends keyof ED, OP extends SelectOption, Cxt extends AsyncContext<ED>>(
|
||||||
|
entity: T,
|
||||||
|
selection: Pick<ED[T]['Selection'], 'filter' | 'count'>,
|
||||||
|
context: Cxt,
|
||||||
|
option: OP): Promise<number>;
|
||||||
|
|
||||||
protected abstract updateAbjointRow<T extends keyof ED, OP extends OperateOption, Cxt extends SyncContext<ED>>(
|
protected abstract updateAbjointRow<T extends keyof ED, OP extends OperateOption, Cxt extends SyncContext<ED>>(
|
||||||
entity: T,
|
entity: T,
|
||||||
|
|
@ -416,13 +428,6 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
|
||||||
context: Cxt,
|
context: Cxt,
|
||||||
option: OP): Promise<Partial<ED[T]['Schema']>[]>;
|
option: OP): Promise<Partial<ED[T]['Schema']>[]>;
|
||||||
|
|
||||||
protected abstract countAsync<T extends keyof ED, OP extends SelectOption, Cxt extends AsyncContext<ED>>(
|
|
||||||
entity: T,
|
|
||||||
selection: Pick<ED[T]['Selection'], 'filter' | 'count'>,
|
|
||||||
context: Cxt,
|
|
||||||
option: OP
|
|
||||||
): Promise<number>;
|
|
||||||
|
|
||||||
protected abstract updateAbjointRowAsync<T extends keyof ED, OP extends OperateOption, Cxt extends AsyncContext<ED>>(
|
protected abstract updateAbjointRowAsync<T extends keyof ED, OP extends OperateOption, Cxt extends AsyncContext<ED>>(
|
||||||
entity: T,
|
entity: T,
|
||||||
operation: ED[T]['Create'] | ED[T]['Update'] | ED[T]['Remove'],
|
operation: ED[T]['Create'] | ED[T]['Update'] | ED[T]['Remove'],
|
||||||
|
|
@ -2169,4 +2174,22 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
|
||||||
await this.reinforceOperation(entity, operation, context, option);
|
await this.reinforceOperation(entity, operation, context, option);
|
||||||
return this.cascadeUpdateAsync(entity, operation, context, option);
|
return this.cascadeUpdateAsync(entity, operation, context, option);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected countSync<T extends keyof ED, OP extends SelectOption, Cxt extends SyncContext<ED>>(
|
||||||
|
entity: T,
|
||||||
|
selection: Pick<ED[T]['Selection'], 'filter' | 'count'>,
|
||||||
|
context: Cxt,
|
||||||
|
option: OP) {
|
||||||
|
this.reinforceSelectionSync(entity, selection as ED[T]['Selection'], context, option, true); // 这样写可能有问题的,虽然能跳过本地的projection补全,但如果有更多的selectionRewriter注入可能会出问题。by Xc 20231220
|
||||||
|
return this.countAbjointRow(entity, selection as ED[T]['Selection'], context, option);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected countAsync<T extends keyof ED, OP extends SelectOption, Cxt extends AsyncContext<ED>>(
|
||||||
|
entity: T,
|
||||||
|
selection: Pick<ED[T]['Selection'], 'filter' | 'count'>,
|
||||||
|
context: Cxt,
|
||||||
|
option: OP) {
|
||||||
|
this.reinforceSelectionAsync(entity, selection as ED[T]['Selection'], context, option, true); // 这样写可能有问题的,虽然能跳过本地的projection补全,但如果有更多的selectionRewriter注入可能会出问题。by Xc 20231220
|
||||||
|
return this.countAbjointRowAsync(entity, selection as ED[T]['Selection'], context, option);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -563,7 +563,7 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict, Cxt extends
|
||||||
const rs = grouped[uuid];
|
const rs = grouped[uuid];
|
||||||
const { [TriggerDataAttribute]: triggerData } = rs[0];
|
const { [TriggerDataAttribute]: triggerData } = rs[0];
|
||||||
const { name, cxtStr, option } = triggerData!;
|
const { name, cxtStr, option } = triggerData!;
|
||||||
await context.initialize(JSON.parse(cxtStr));
|
// await context.initialize(JSON.parse(cxtStr)); // 这里token有可能过期(用户注销),先用root态模拟吧
|
||||||
await this.execVolatileTrigger(entity, name, rs.map(ele => ele.id!), context, option);
|
await this.execVolatileTrigger(entity, name, rs.map(ele => ele.id!), context, option);
|
||||||
}
|
}
|
||||||
await context.commit();
|
await context.commit();
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ export class SimpleConnector<ED extends EntityDict, FrontCxt extends SyncContext
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// fetch返回异常一定是网络异常
|
// fetch返回异常一定是网络异常
|
||||||
throw new OakNetworkException();
|
throw new OakNetworkException(`请求[${this.serverAspectUrl}],发生网络异常`);
|
||||||
}
|
}
|
||||||
if (response.status > 299) {
|
if (response.status > 299) {
|
||||||
const err = new OakServerProxyException(
|
const err = new OakServerProxyException(
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
import { generateNewId } from '../src/utils/uuid';
|
/* import { generateNewId } from '../src/utils/uuid';
|
||||||
|
|
||||||
let iter = 20;
|
let iter = 20;
|
||||||
|
|
||||||
while (iter > 0) {
|
while (iter > 0) {
|
||||||
console.log(generateNewId());
|
console.log(generateNewId());
|
||||||
iter --;
|
iter --;
|
||||||
}
|
} */
|
||||||
|
import { join } from 'path';
|
||||||
|
import { buildRouter } from '../src/compiler/routerBuilder';
|
||||||
|
|
||||||
|
buildRouter(join(process.cwd(), '..', 'taicang'), 'web', true);
|
||||||
Loading…
Reference in New Issue