Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/unittest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ jobs:
unittest:
name: Unit Tests (${{ matrix.shard }}/3)
runs-on: ubuntu-24.04
timeout-minutes: 30

strategy:
fail-fast: false
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ coverage/
/packages/kit/src/components/WebView/translateInject.text-js
/packages/kit/src/components/LightweightChart/utils/lightweightChartsStandalone.text-js
/packages/shared/src/web/index.html
/packages/kit/src/routes/generated/
/mobile/android/build-logic/privacy-security-plugins/bin/main/onekey/privacy/jpush
apps/mobile/android/.kotlin/
apps/mobile/android/build-logic/privacy-security-plugins/bin/main/onekey/privacy/**/*.class
Expand Down
3 changes: 2 additions & 1 deletion apps/cli/src/__tests__/ci-gate-script.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ describe('BotWallet CI gate script', () => {
);
expect(rootPackageJson.scripts['ci:gate']).toBe(
[
'yarn routes:generate',
'yarn lint:staged',
'yarn tsc:staged',
'yarn tsc:staged:no-routes',
'yarn test:unit',
'bash apps/cli/scripts/audit-persistence-fields.sh',
'yarn test:integration:cli',
Expand Down
10 changes: 9 additions & 1 deletion apps/mobile/metro.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,21 @@ const { getSentryExpoConfig } = require('@sentry/react-native/metro');
const connect = require('connect');
const fs = require('fs-extra');
const { resolve } = require('metro-resolver');

const buildTimeEnv = require('@onekeyhq/shared/src/buildTimeEnv');
// const { withRozeniteExpoAtlasPlugin } = require('@rozenite/expo-atlas-plugin'); // Uncomment if needed

const projectRoot = __dirname;

// Pre-calculate monorepo root for use in multiple places
const monorepoRoot = path.resolve(projectRoot, '../..');
const {
ensureRoutePathConfig,
watchRoutePathConfig,
} = require('../../development/scripts/ensure-route-path-config');

ensureRoutePathConfig(['ios', 'android', 'native']);
watchRoutePathConfig(['ios', 'android', 'native']);

// Get Metro's default config for the project
const defaultConfig = getDefaultConfig(projectRoot);
Expand Down Expand Up @@ -324,7 +333,6 @@ if (process.env.RN_HARNESS === 'true') {
};
}

const buildTimeEnv = require('@onekeyhq/shared/src/buildTimeEnv');
const getMetroRuntimeTarget = (context) =>
context.customResolverOptions?.runtimeTarget ||
process.env.METRO_RUNTIME_TARGET ||
Expand Down
92 changes: 23 additions & 69 deletions apps/web/scripts/check-startup-graph-budget.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,15 @@ const {
printAiTriageInstructions,
writeAiHints,
} = require(path.join(repoRoot, 'development/perf-ci/lib/budgetAiHints'));
const { resolveSourceGuardPolicies } = require(
path.join(repoRoot, 'development/perf-ci/lib/budgetConfig'),
);
const { createStaticImportChainReport } = require(
path.join(repoRoot, 'development/perf-ci/lib/importChain'),
);
const { findForbiddenModules, getMissingSourceMaps, getModuleRows } = require(
path.join(repoRoot, 'development/perf-ci/lib/sourceMapSourceGuard'),
);

const DEFAULT_BUDGETS = {
moduleCount: 3500,
Expand Down Expand Up @@ -152,33 +158,6 @@ function walkFiles(dir) {
});
}

function normalizeSource(source) {
return source
.replace(/^webpack:\/\/[^/]+\//, '')
.replace(/^webpack:\/\//, '')
.replace(/^(\.\.\/)+/, '')
.replace(/^\.\//, '');
}

function categorizeModule(source) {
if (source.includes('node_modules/')) return 'node_modules';
if (source.includes('packages/components/')) return 'components';
if (source.includes('packages/kit-bg/')) return 'kit-bg';
if (source.includes('packages/kit/')) return 'kit';
if (source.includes('packages/shared/')) return 'shared';
if (source.includes('apps/web/')) return 'apps/web';
return 'other';
}

function getPackageName(source) {
const marker = 'node_modules/';
const index = source.indexOf(marker);
if (index < 0) return null;
const parts = source.slice(index + marker.length).split('/');
if (!parts[0]) return null;
return parts[0].startsWith('@') ? parts.slice(0, 2).join('/') : parts[0];
}

function getFileSizeRows(files) {
return files.map((file) => {
const filePath = path.join(buildDir, file);
Expand All @@ -192,40 +171,6 @@ function getFileSizeRows(files) {
});
}

function getModuleRows(scriptFiles) {
const modules = new Map();
for (const file of scriptFiles) {
const mapPath = path.join(buildDir, `${file}.map`);
if (fs.existsSync(mapPath)) {
const map = JSON.parse(fs.readFileSync(mapPath, 'utf8'));
const sources = map.sources || [];
const contents = map.sourcesContent || [];
for (let index = 0; index < sources.length; index += 1) {
const source = normalizeSource(sources[index]);
const bytes = Buffer.byteLength(contents[index] || '');
const existing = modules.get(source);
if (existing) {
existing.bytes = Math.max(existing.bytes, bytes);
existing.files.add(file);
} else {
modules.set(source, {
source,
bytes,
category: categorizeModule(source),
packageName: getPackageName(source),
files: new Set([file]),
});
}
}
}
}

return [...modules.values()].map((module) => ({
...module,
files: [...module.files],
}));
}

function sum(rows, key) {
return rows.reduce((total, row) => total + (Number(row[key]) || 0), 0);
}
Expand Down Expand Up @@ -270,20 +215,28 @@ function main() {

const budgetConfig = readJsonIfExists(budgetPath);
const budgets = loadBudgets();
const { initialForbiddenSources } = resolveSourceGuardPolicies({
startupGraph: budgetConfig?.startupGraph,
defaultInitialForbiddenSources: DEFAULT_FORBIDDEN_SOURCES,
});
const forbiddenSources = csvEnv(
'WEB_STARTUP_FORBIDDEN_SOURCES',
budgetConfig?.startupGraph?.forbiddenSources || DEFAULT_FORBIDDEN_SOURCES,
initialForbiddenSources,
);

const html = fs.readFileSync(htmlPath, 'utf8');
const initialScriptFiles = getInitialScriptFiles(html).filter((file) =>
fs.existsSync(path.join(buildDir, file)),
);
const missingSourceMaps = initialScriptFiles.filter(
(file) => !fs.existsSync(path.join(buildDir, `${file}.map`)),
);
const missingSourceMaps = getMissingSourceMaps({
buildDir,
scriptFiles: initialScriptFiles,
});
const initialScripts = getFileSizeRows(initialScriptFiles);
const moduleRows = getModuleRows(initialScriptFiles);
const moduleRows = getModuleRows({
buildDir,
scriptFiles: initialScriptFiles,
});
const allScriptFiles = walkFiles(buildDir).filter(
(file) => /\.m?js$/.test(file) && !file.endsWith('.map'),
);
Expand All @@ -307,9 +260,10 @@ function main() {
};

const packageBytes = groupBytes(moduleRows, 'packageName');
const forbiddenModulesFound = moduleRows.filter((row) =>
forbiddenSources.some((pattern) => row.source.includes(pattern)),
);
const forbiddenModulesFound = findForbiddenModules({
moduleRows,
forbiddenSources,
});

const budgetChecks = [
makeBudgetCheck('moduleCount', summary.moduleCount, budgets.moduleCount),
Expand Down
6 changes: 6 additions & 0 deletions development/lint/ts.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ const { exit } = require('process');

const { parse } = require('@aivenio/tsc-output-parser');

const {
ensureRoutePathConfig,
} = require('../scripts/ensure-route-path-config');

ensureRoutePathConfig(['native']);

const getTimestamp = () => new Date().toLocaleTimeString();
const startTime = Date.now();

Expand Down
56 changes: 56 additions & 0 deletions development/perf-ci/lib/budgetConfig.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
function normalizeBudgetConfig(raw, defaultBudgets) {
if (raw?.defaults || raw?.scenarios || raw?.startupGraph) {
return {
...raw,
defaults: {
...defaultBudgets,
...raw.defaults,
},
scenarios: raw.scenarios || {},
};
}

return {
defaults: {
...defaultBudgets,
...raw,
},
scenarios: {},
};
}

function uniqueStrings(values) {
return [
...new Set(values.filter((value) => typeof value === 'string' && value)),
];
}

function resolveSourceGuardPolicies({
startupGraph,
defaultInitialForbiddenSources = [],
}) {
const initialOnlyForbiddenSources = Array.isArray(
startupGraph?.forbiddenSources,
)
? startupGraph.forbiddenSources
: defaultInitialForbiddenSources;
const requestedScriptForbiddenSources = uniqueStrings(
Array.isArray(startupGraph?.requestedScriptForbiddenSources)
? startupGraph.requestedScriptForbiddenSources
: [],
);

// Sources forbidden at runtime must also stay out of the initial entry graph.
return {
initialForbiddenSources: uniqueStrings([
...initialOnlyForbiddenSources,
...requestedScriptForbiddenSources,
]),
requestedScriptForbiddenSources,
};
}

module.exports = {
normalizeBudgetConfig,
resolveSourceGuardPolicies,
};
58 changes: 58 additions & 0 deletions development/perf-ci/lib/budgetConfig.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
const {
normalizeBudgetConfig,
resolveSourceGuardPolicies,
} = require('./budgetConfig');

describe('normalizeBudgetConfig', () => {
const defaults = { scriptCount: 10, resourceCount: 20 };

it('preserves startup graph guards in structured budget files', () => {
const startupGraph = {
forbiddenSources: ['packages/kit/src/views/Onboardingv2/pages/'],
moduleCount: 100,
};

expect(
normalizeBudgetConfig(
{
defaults: { scriptCount: 8 },
startupGraph,
scenarios: { root: { resourceCount: 15 } },
},
defaults,
),
).toEqual({
defaults: { scriptCount: 8, resourceCount: 20 },
startupGraph,
scenarios: { root: { resourceCount: 15 } },
});
});

it('keeps legacy flat budget files compatible', () => {
expect(normalizeBudgetConfig({ scriptCount: 8 }, defaults)).toEqual({
defaults: { scriptCount: 8, resourceCount: 20 },
scenarios: {},
});
});

it('keeps initial-only sources out of the runtime requested-script policy', () => {
expect(
resolveSourceGuardPolicies({
startupGraph: {
forbiddenSources: ['@reown/'],
requestedScriptForbiddenSources: [
'packages/kit/src/views/Onboardingv2/router/',
],
},
}),
).toEqual({
initialForbiddenSources: [
'@reown/',
'packages/kit/src/views/Onboardingv2/router/',
],
requestedScriptForbiddenSources: [
'packages/kit/src/views/Onboardingv2/router/',
],
});
});
});
Loading
Loading