diff --git a/build/analyze-dependencies.js b/build/analyze-dependencies.js deleted file mode 100644 index 81acec7d..00000000 --- a/build/analyze-dependencies.js +++ /dev/null @@ -1,129 +0,0 @@ -const fs = require('fs'); -const path = require('path'); -const webpack = require('webpack'); -const webpackConfig = require('../webpack.config.js'); - -class DependencyAnalyzer { - constructor() { - this.dependencies = { - background: new Set(), - ui: new Set(), - content: new Set(), - shared: new Set(), - }; - } - - analyzeDependencies(stats) { - const modules = new Map(); - - // Use compilation.modules instead of chunk.getModules() - stats.compilation.modules.forEach((module) => { - if (!module.resource || !module.resource.includes('node_modules')) { - return; - } - - const pkg = module.resource.split('node_modules/')[1].split('/')[0]; - const chunks = Array.from(module.chunksIterable || []); - - chunks.forEach((chunk) => { - const target = chunk.name?.includes('background') - ? 'background' - : chunk.name?.includes('ui') - ? 'ui' - : chunk.name?.includes('content') - ? 'content' - : null; - - if (target) { - if (!modules.has(pkg)) { - modules.set(pkg, new Set()); - } - modules.get(pkg).add(target); - } - }); - }); - - // Categorize dependencies - modules.forEach((targets, pkg) => { - if (targets.size > 1) { - this.dependencies.shared.add(pkg); - } else { - const [target] = targets; - this.dependencies[target].add(pkg); - } - }); - - return this; - } - - generateReport() { - let report = '# Extension Dependencies Analysis\n\n'; - report += `> Generated on ${new Date().toLocaleString()}\n\n`; - - // Add summary - report += '## Summary\n\n'; - Object.entries(this.dependencies).forEach(([target, deps]) => { - report += `- **${target}**: ${deps.size} packages\n`; - }); - report += '\n'; - - // Add detailed lists - Object.entries(this.dependencies).forEach(([target, deps]) => { - report += `## ${target.charAt(0).toUpperCase() + target.slice(1)} Dependencies\n\n`; - if (deps.size === 0) { - report += '_No dependencies_\n\n'; - } else { - Array.from(deps) - .sort() - .forEach((dep) => { - report += `- \`${dep}\`\n`; - }); - report += '\n'; - } - }); - - return report; - } -} - -// Prepare build environment -console.log('Preparing build environment...'); - -// Copy manifest -fs.copyFileSync( - path.join(__dirname, '../_raw/manifest/manifest.dev.json'), - path.join(__dirname, '../_raw/manifest.json') -); - -// Clean dist directory -const distPath = path.join(__dirname, '../dist'); -if (!fs.existsSync(distPath)) { - fs.mkdirSync(distPath); -} else { - fs.rmSync(distPath, { recursive: true }); - fs.mkdirSync(distPath); -} - -// Copy _raw contents to dist -fs.cpSync(path.join(__dirname, '../_raw'), distPath, { recursive: true }); - -// Get webpack config using the same configuration as build:dev -const config = webpackConfig({ config: 'dev' }); - -// Run the analysis -console.log('Starting webpack build and analysis...'); -const analyzer = new DependencyAnalyzer(); - -webpack(config, (err, stats) => { - if (err || stats.hasErrors()) { - console.error('Build failed:', err || stats.toString()); - process.exit(1); - } - - console.log('Build complete, analyzing dependencies...'); - const report = analyzer.analyzeDependencies(stats).generateReport(); - - const reportPath = path.join(__dirname, '../extension-dependencies.md'); - fs.writeFileSync(reportPath, report); - console.log(`Analysis complete! Check ${reportPath}`); -}); diff --git a/build/analyze-dependencies.ts b/build/analyze-dependencies.ts new file mode 100644 index 00000000..3a5db531 --- /dev/null +++ b/build/analyze-dependencies.ts @@ -0,0 +1,243 @@ +import fs from 'fs'; +import path from 'path'; + +import { parse } from '@babel/parser'; +import traverseDefault from '@babel/traverse'; +import glob from 'glob'; +import webpack from 'webpack'; + +import webpackConfig from '../webpack.config.js'; + +type Dependencies = { + background: Set; + ui: Set; + content: Set; + shared: Set; +}; + +function analyzeDependencies(stats: webpack.Stats): Dependencies { + const modules = new Map>(); + const dependencies: Dependencies = { + background: new Set(), + ui: new Set(), + content: new Set(), + shared: new Set(), + }; + + stats.compilation.modules.forEach((module) => { + if ( + !(module instanceof webpack.NormalModule) || + !module.resource || + !module.resource.includes('node_modules') + ) { + return; + } + + const pkg = module.resource.split('node_modules/')[1].split('/')[0]; + const chunks = Array.from(module.chunksIterable || []); + + chunks.forEach((chunk) => { + const target = chunk.name?.includes('background') + ? 'background' + : chunk.name?.includes('ui') + ? 'ui' + : chunk.name?.includes('content') + ? 'content' + : null; + + if (target) { + if (!modules.has(pkg)) { + modules.set(pkg, new Set()); + } + modules.get(pkg)?.add(target); + } + }); + }); + + // Categorize dependencies + modules.forEach((targets, pkg) => { + if (targets.size > 1) { + dependencies.shared.add(pkg); + } else { + const [target] = targets; + dependencies[target].add(pkg); + } + }); + + return dependencies; +} + +function findSourceImports(): Set { + const imports = new Set(); + const files = glob.sync('src/**/*.{ts,tsx,js,jsx}'); + + files.forEach((file) => { + const content = fs.readFileSync(file, 'utf-8'); + try { + const ast = parse(content, { + sourceType: 'module', + plugins: [ + 'typescript', + 'jsx', + 'decorators-legacy', + 'classProperties', + 'classPrivateProperties', + 'classPrivateMethods', + 'exportDefaultFrom', + 'exportNamespaceFrom', + 'throwExpressions', + 'dynamicImport', + ], + }); + + traverseDefault(ast, { + ImportDeclaration(path) { + const importPath = path.node.source.value; + if (!importPath.startsWith('.') && !importPath.startsWith('@/')) { + const packageName = importPath.startsWith('@') + ? importPath.split('/').slice(0, 2).join('/') + : importPath.split('/')[0]; + imports.add(packageName); + } + }, + }); + } catch (error) { + console.warn(`Failed to parse ${file}:`, error.message); + } + }); + + return imports; +} + +type UnusedPackageInfo = { + name: string; + isDev: boolean; +}; + +function findUnusedPackages(dependencies: Dependencies): UnusedPackageInfo[] { + const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf-8')); + const prodDeps = new Set(Object.keys(packageJson.dependencies || {})); + const devDeps = new Set(Object.keys(packageJson.devDependencies || {})); + + const usedPackages = new Set([ + ...dependencies.background, + ...dependencies.ui, + ...dependencies.content, + ...dependencies.shared, + ...findSourceImports(), + ]); + + return [...prodDeps, ...devDeps] + .filter((pkg) => !usedPackages.has(pkg)) + .map((pkg) => ({ + name: pkg, + isDev: devDeps.has(pkg), + })); +} + +function generateReport(dependencies: Dependencies): string { + let report = '# Extension Dependencies Analysis\n\n'; + report += `> Generated on ${new Date().toLocaleString()}\n\n`; + + // Add summary + report += '## Summary\n\n'; + Object.entries(dependencies).forEach(([target, deps]) => { + report += `- **${target}**: ${deps.size} packages\n`; + }); + report += '\n'; + + // Add detailed lists + Object.entries(dependencies).forEach(([target, deps]) => { + report += `## ${target.charAt(0).toUpperCase() + target.slice(1)} Dependencies\n\n`; + if (deps.size === 0) { + report += '_No dependencies_\n\n'; + } else { + Array.from(deps) + .sort() + .forEach((dep) => { + report += `- \`${dep}\`\n`; + }); + report += '\n'; + } + }); + + // Add unused packages section + const unusedPackages = findUnusedPackages(dependencies); + report += '## Unused Packages\n\n'; + + if (unusedPackages.length === 0) { + report += '_All declared packages are used_\n\n'; + } else { + report += 'The following packages are declared in package.json but not used in the build:\n\n'; + + // Group by prod/dev + const prodUnused = unusedPackages.filter((p) => !p.isDev); + const devUnused = unusedPackages.filter((p) => p.isDev); + + if (prodUnused.length > 0) { + report += '### Production Dependencies\n\n'; + prodUnused.forEach((pkg) => { + report += `- \`${pkg.name}\`\n`; + }); + report += '\n'; + } + + if (devUnused.length > 0) { + report += '### Development Dependencies\n\n'; + devUnused.forEach((pkg) => { + report += `- \`${pkg.name}\`\n`; + }); + report += '\n'; + } + } + + return report; +} + +function prepareBuildEnvironment() { + // eslint-disable-next-line no-console + console.log('Preparing build environment...'); + + // Copy manifest + fs.copyFileSync( + path.join(__dirname, '../_raw/manifest/manifest.dev.json'), + path.join(__dirname, '../_raw/manifest.json') + ); + + // Clean dist directory + const distPath = path.join(__dirname, '../dist'); + if (!fs.existsSync(distPath)) { + fs.mkdirSync(distPath); + } else { + fs.rmSync(distPath, { recursive: true }); + fs.mkdirSync(distPath); + } + + // Copy _raw contents to dist + fs.cpSync(path.join(__dirname, '../_raw'), distPath, { recursive: true }); +} + +// Run the analysis +prepareBuildEnvironment(); + +const config = webpackConfig({ config: 'dev' }); +config.watch = false; +// eslint-disable-next-line no-console +console.log('Starting webpack build and analysis...'); + +webpack(config, (err, stats) => { + if (err || !stats || (stats && stats.hasErrors())) { + console.error('Build failed:', err || stats?.toString()); + process.exit(1); + } + + // eslint-disable-next-line no-console + console.log('Build complete, analyzing dependencies...'); + const dependencies = analyzeDependencies(stats); + const report = generateReport(dependencies); + + const reportPath = path.join(__dirname, '../extension-dependencies.md'); + fs.writeFileSync(reportPath, report); + // eslint-disable-next-line no-console + console.log(`Analysis complete! Check ${reportPath}`); +}); diff --git a/build/analyze-imports.js b/build/analyze-imports.js deleted file mode 100644 index 05810eef..00000000 --- a/build/analyze-imports.js +++ /dev/null @@ -1,136 +0,0 @@ -const fs = require('fs'); -const path = require('path'); -const glob = require('glob'); -const parser = require('@babel/parser'); -const traverse = require('@babel/traverse').default; - -class ImportAnalyzer { - constructor() { - this.imports = new Map(); // package -> Set of files using it - this.packageLocations = { - background: new Set(), - ui: new Set(), - content: new Set(), - }; - } - - analyzeFile(filePath) { - const content = fs.readFileSync(filePath, 'utf-8'); - const relativePath = path.relative(process.cwd(), filePath); - - try { - const ast = parser.parse(content, { - sourceType: 'module', - plugins: ['typescript', 'jsx'], - }); - - traverse(ast, { - ImportDeclaration: (path) => { - const importPath = path.node.source.value; - - // Only analyze external packages (not relative imports) - if (!importPath.startsWith('.') && !importPath.startsWith('@/')) { - const packageName = importPath.startsWith('@') - ? importPath.split('/').slice(0, 2).join('/') - : importPath.split('/')[0]; - - if (!this.imports.has(packageName)) { - this.imports.set(packageName, new Set()); - } - this.imports.get(packageName).add(relativePath); - - // Categorize by location - if (relativePath.includes('background/')) { - this.packageLocations.background.add(packageName); - } else if (relativePath.includes('ui/')) { - this.packageLocations.ui.add(packageName); - } else if (relativePath.includes('content/')) { - this.packageLocations.content.add(packageName); - } - } - }, - CallExpression(path) { - // Check for require() calls - if (path.node.callee.name === 'require') { - const arg = path.node.arguments[0]; - if (arg && arg.type === 'StringLiteral') { - const importPath = arg.value; - if (!importPath.startsWith('.') && !importPath.startsWith('@/')) { - const packageName = importPath.startsWith('@') - ? importPath.split('/').slice(0, 2).join('/') - : importPath.split('/')[0]; - - if (!this.imports.has(packageName)) { - this.imports.set(packageName, new Set()); - } - this.imports.get(packageName).add(relativePath); - - // Categorize by location - if (relativePath.includes('background/')) { - this.packageLocations.background.add(packageName); - } else if (relativePath.includes('ui/')) { - this.packageLocations.ui.add(packageName); - } else if (relativePath.includes('content/')) { - this.packageLocations.content.add(packageName); - } - } - } - } - }, - }); - } catch (error) { - console.warn(`Failed to parse ${relativePath}:`, error.message); - } - } - - generateReport() { - let report = '# Dependency Usage Analysis\n\n'; - report += `> Generated on ${new Date().toLocaleString()}\n\n`; - - // Summary by location - report += '## Package Usage by Location\n\n'; - Object.entries(this.packageLocations).forEach(([location, packages]) => { - report += `### ${location.charAt(0).toUpperCase() + location.slice(1)}\n\n`; - Array.from(packages) - .sort() - .forEach((pkg) => { - const usageCount = this.imports.get(pkg).size; - report += `- \`${pkg}\` (${usageCount} ${usageCount === 1 ? 'file' : 'files'})\n`; - }); - report += '\n'; - }); - - // Detailed usage - report += '## Detailed Package Usage\n\n'; - Array.from(this.imports.entries()) - .sort(([a], [b]) => a.localeCompare(b)) - .forEach(([pkg, files]) => { - report += `### \`${pkg}\`\n\n`; - Array.from(files) - .sort() - .forEach((file) => { - report += `- ${file}\n`; - }); - report += '\n'; - }); - - return report; - } -} - -// Run the analysis -console.log('Starting import analysis...'); -const analyzer = new ImportAnalyzer(); - -// Find all TypeScript and JavaScript files -const files = glob.sync('src/**/*.{ts,tsx,js,jsx}', { - ignore: ['**/node_modules/**', '**/dist/**'], -}); - -files.forEach((file) => { - analyzer.analyzeFile(file); -}); - -const report = analyzer.generateReport(); -fs.writeFileSync('dependency-usage.md', report); -console.log('Analysis complete! Check dependency-usage.md'); diff --git a/build/analyze-imports.ts b/build/analyze-imports.ts new file mode 100644 index 00000000..d4c7dbfe --- /dev/null +++ b/build/analyze-imports.ts @@ -0,0 +1,121 @@ +import fs from 'fs'; +import path from 'path'; + +import parser from '@babel/parser'; +import traverse from '@babel/traverse'; +import glob from 'glob'; + +type PackageLocations = { + background: Set; + ui: Set; + content: Set; +}; + +function analyzeFile( + filePath: string, + imports: Map>, + packageLocations: PackageLocations +) { + const content = fs.readFileSync(filePath, 'utf-8'); + const relativePath = path.relative(process.cwd(), filePath); + + try { + const ast = parser.parse(content, { + sourceType: 'module', + plugins: ['typescript', 'jsx'], + }); + + const processImport = (importPath: string) => { + if (!importPath.startsWith('.') && !importPath.startsWith('@/')) { + const packageName = importPath.startsWith('@') + ? importPath.split('/').slice(0, 2).join('/') + : importPath.split('/')[0]; + + if (!imports.has(packageName)) { + imports.set(packageName, new Set()); + } + imports.get(packageName)?.add(relativePath); + + if (relativePath.includes('background/')) { + packageLocations.background.add(packageName); + } else if (relativePath.includes('ui/')) { + packageLocations.ui.add(packageName); + } else if (relativePath.includes('content/')) { + packageLocations.content.add(packageName); + } + } + }; + + traverse(ast, { + ImportDeclaration: (path) => { + processImport(path.node.source.value); + }, + CallExpression(path) { + if (path.node.callee.type === 'Identifier' && path.node.callee.name === 'require') { + const arg = path.node.arguments[0]; + if (arg && arg.type === 'StringLiteral') { + processImport(arg.value); + } + } + }, + }); + } catch (error) { + console.warn(`Failed to parse ${relativePath}:`, error.message); + } +} + +function generateReport(imports: Map>, packageLocations: PackageLocations) { + let report = '# Dependency Usage Analysis\n\n'; + report += `> Generated on ${new Date().toLocaleString()}\n\n`; + + report += '## Package Usage by Location\n\n'; + Object.entries(packageLocations).forEach(([location, packages]) => { + report += `### ${location.charAt(0).toUpperCase() + location.slice(1)}\n\n`; + Array.from(packages) + .sort() + .forEach((pkg) => { + const usageCount = imports.get(pkg)?.size ?? 0; + report += `- \`${pkg}\` (${usageCount} ${usageCount === 1 ? 'file' : 'files'})\n`; + }); + report += '\n'; + }); + + report += '## Detailed Package Usage\n\n'; + Array.from(imports.entries()) + .sort(([a], [b]) => a.localeCompare(b)) + .forEach(([pkg, files]) => { + report += `### \`${pkg}\`\n\n`; + Array.from(files) + .sort() + .forEach((file) => { + report += `- ${file}\n`; + }); + report += '\n'; + }); + + return report; +} + +// Run the analysis +// eslint-disable-next-line no-console +console.log('Starting import analysis...'); + +const imports = new Map>(); +const packageLocations: PackageLocations = { + background: new Set(), + ui: new Set(), + content: new Set(), +}; + +const files = glob.sync('src/**/*.{ts,tsx,js,jsx}', { + ignore: ['**/node_modules/**', '**/dist/**'], +}); + +files.forEach((file) => { + analyzeFile(file, imports, packageLocations); +}); + +const report = generateReport(imports, packageLocations); +fs.writeFileSync('dependency-usage.md', report); +// eslint-disable-next-line no-console +console.log('Analysis complete! Check dependency-usage.md'); diff --git a/build/paths.js b/build/paths.ts similarity index 83% rename from build/paths.js rename to build/paths.ts index 94368b09..6558ac6c 100644 --- a/build/paths.js +++ b/build/paths.ts @@ -1,11 +1,11 @@ -const path = require('path'); -const fs = require('fs'); +import fs from 'fs'; +import path from 'path'; const appRoot = fs.realpathSync(process.cwd()); const rootResolve = path.resolve.bind(path, appRoot); -module.exports = { +const paths = { root: appRoot, src: rootResolve('src'), popupHtml: rootResolve('src/ui/popup.html'), @@ -16,3 +16,5 @@ module.exports = { rootResolve, }; + +export default paths; diff --git a/build/prepareManifest.js b/build/prepareManifest.ts similarity index 74% rename from build/prepareManifest.js rename to build/prepareManifest.ts index c5149a70..c2458cac 100644 --- a/build/prepareManifest.js +++ b/build/prepareManifest.ts @@ -1,19 +1,20 @@ -const path = require('path'); -const fs = require('fs-extra'); -const http = require('http'); -// require('dotenv').config(); +import fs from 'fs'; +import http from 'http'; +import path from 'path'; + +import dotenv from 'dotenv'; const PROJECT_ROOT = path.resolve(__dirname, '..'); const args = process.argv.slice(2); const mode = args[0]; -require('dotenv').config({ path: `.env.${mode}` }); +dotenv.config({ path: `.env.${mode}` }); const OAUTH2_SCOPES = process.env.OAUTH2_SCOPES || ''; const DEVTOOLS_URL = 'http://localhost:8097'; -async function fetchDevTools() { +async function fetchDevTools(): Promise { return new Promise((resolve, reject) => { const request = http.get(DEVTOOLS_URL, (response) => { if (response.statusCode !== 200) { @@ -39,24 +40,22 @@ async function fetchDevTools() { } async function prepare() { - console.log(process.env.NODE_ENV); - console.log(process.env.OAUTH2_CLIENT_ID); - const manifestPath = path.resolve(PROJECT_ROOT, '_raw', 'manifest.json'); - const manifest = fs.readJSONSync(manifestPath); + const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8')); manifest.oauth2 = { client_id: process.env.OAUTH2_CLIENT_ID, scopes: OAUTH2_SCOPES.split(','), }; - if (mode == 'dev') { + if (mode === 'dev') { manifest.key = process.env.MANIFEST_KEY; try { const devToolsScript = await fetchDevTools(); fs.writeFileSync(path.resolve(__dirname, '../_raw/react-devtools.js'), devToolsScript); - console.log('✅ React DevTools source fetched successfully'); - } catch (error) { + // eslint-disable-next-line no-console + console.info('✅ React DevTools source fetched successfully'); + } catch { console.warn('⚠️ Failed to fetch React DevTools. Run the devtools server first'); // Write empty file if fetch fails fs.writeFileSync( @@ -66,9 +65,9 @@ async function prepare() { } } - fs.writeJSONSync(manifestPath, manifest, { spaces: 2 }); + fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2)); - return ''; + return manifest; } prepare(); diff --git a/build/release.js b/build/release.ts similarity index 60% rename from build/release.js rename to build/release.ts index ed85f499..e7fdaa62 100644 --- a/build/release.js +++ b/build/release.ts @@ -1,21 +1,24 @@ -const path = require('path'); -const { prompt: enquirerPrompt } = require('enquirer'); -const fs = require('fs-extra'); -const shell = require('shelljs'); -const zipdir = require('zip-dir'); +import fs from 'fs'; +import path from 'path'; + +import { prompt } from 'enquirer'; +import shell from 'shelljs'; +import zipdir from 'zip-dir'; const PROJECT_ROOT = path.resolve(__dirname, '..'); +type Version = `${number}.${number}.${number}`; -async function release() { - const input = await enquirerPrompt({ +async function release(): Promise { + const input: { version: Version } = await prompt({ type: 'input', name: 'version', message: '[Flow Wallet] Please input the release version:', }); const manifestPath = path.resolve(PROJECT_ROOT, '_raw', 'manifest.json'); - const manifest = fs.readJSONSync(manifestPath); + const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8')); + manifest.version = input.version; - fs.writeJSONSync(manifestPath, manifest, { spaces: 2 }); + fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2)); shell.exec(`npm version ${input.version} --force`); shell.exec('git add -A'); shell.exec(`git commit -m "[release] ${input.version}"`); @@ -25,7 +28,7 @@ async function release() { return input.version; } -function bundle(version) { +function bundle(version: Version) { shell.exec('pnpm build:pro'); const distPath = path.resolve(PROJECT_ROOT, 'dist'); zipdir(distPath, { saveTo: `FlowCore_${version}.zip` }); diff --git a/build/webpack.common.config.js b/build/webpack.common.config.ts similarity index 91% rename from build/webpack.common.config.js rename to build/webpack.common.config.ts index cf0ffb80..a7d52d82 100644 --- a/build/webpack.common.config.js +++ b/build/webpack.common.config.ts @@ -1,15 +1,17 @@ -const webpack = require('webpack'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); -const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin'); -const { version } = require('../_raw/manifest.json'); -const path = require('path'); -const CopyPlugin = require('copy-webpack-plugin'); -const fs = require('fs'); +import fs from 'fs'; +import path from 'path'; -const paths = require('./paths'); +import CopyPlugin from 'copy-webpack-plugin'; +import HtmlWebpackPlugin from 'html-webpack-plugin'; +import TsconfigPathsPlugin from 'tsconfig-paths-webpack-plugin'; +import webpack from 'webpack'; -const config = (env) => { - console.log('env', env); +import packageJson from '../package.json'; + +import paths from './paths'; +const { version } = packageJson; + +const config = (env: { config: 'dev' | 'pro' | 'none' }): webpack.Configuration => { const isDevelopment = env.config === 'dev'; const devToolsExists = isDevelopment && fs.existsSync(path.resolve(__dirname, '../_raw/react-devtools.js')); @@ -195,4 +197,4 @@ const config = (env) => { }; }; -module.exports = config; +export default config; diff --git a/build/webpack.dev.config.js b/build/webpack.dev.config.ts similarity index 76% rename from build/webpack.dev.config.js rename to build/webpack.dev.config.ts index 336b2138..163c516d 100644 --- a/build/webpack.dev.config.js +++ b/build/webpack.dev.config.ts @@ -1,14 +1,14 @@ -const webpack = require('webpack'); -const Dotenv = require('dotenv-webpack'); -const CopyPlugin = require('copy-webpack-plugin'); +import CopyPlugin from 'copy-webpack-plugin'; +import Dotenv from 'dotenv-webpack'; +import webpack from 'webpack'; // for extension local test, can build each time -const config = { +const config: webpack.Configuration = { mode: 'development', devtool: 'inline-cheap-module-source-map', watch: true, cache: { - type: 'filesystem', + type: 'filesystem' as const, }, watchOptions: { ignored: ['**/public', '**/node_modules'], @@ -38,4 +38,4 @@ const config = { }, }; -module.exports = config; +export default config; diff --git a/build/webpack.pro.config.js b/build/webpack.pro.config.ts similarity index 69% rename from build/webpack.pro.config.js rename to build/webpack.pro.config.ts index aa7fbc3f..ecf87dd5 100644 --- a/build/webpack.pro.config.js +++ b/build/webpack.pro.config.ts @@ -1,13 +1,11 @@ -const webpack = require('webpack'); -const path = require('path'); -// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; -const Dotenv = require('dotenv-webpack'); +import Dotenv from 'dotenv-webpack'; +import webpack from 'webpack'; -const config = { +const config: webpack.Configuration = { mode: 'production', devtool: false, cache: { - type: 'filesystem', + type: 'filesystem' as const, }, performance: { maxEntrypointSize: 2500000, @@ -35,4 +33,4 @@ const config = { }, }; -module.exports = config; +export default config; diff --git a/eslint.config.mjs b/eslint.config.mjs index 4afdbe92..a76f051c 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -9,7 +9,7 @@ import importPlugin from 'eslint-plugin-import'; export default [ // Base config for all files { - ignores: ['**/dist/**', '**/build/**', '**/node_modules/**', '**/.git/**', '**/coverage/**'], + ignores: ['**/dist/**', '**/node_modules/**', '**/.git/**', '**/coverage/**'], }, // JavaScript and TypeScript files diff --git a/package.json b/package.json index a49cc7e3..83b69234 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,8 @@ "version": "2.6.3", "description": "Digital wallet created for everyone.", "scripts": { - "prepare:dev": "node ./build/prepareManifest.js dev", - "prepare:pro": "node ./build/prepareManifest.js pro", + "prepare:dev": "pnpx tsx ./build/prepareManifest.ts dev", + "prepare:pro": "pnpx tsx ./build/prepareManifest.ts pro", "clean": "mkdir -p dist && rm -rf dist/* && cp -r _raw/* dist", "winClean": "if not exist dist mkdir dist && del /Q /F dist\\* && xcopy /E /I _raw dist", "winBuild:dev": "set NODE_OPTIONS=--max-old-space-size=8192 && copy _raw\\manifest\\manifest.dev.json _raw\\manifest.json && pnpm run prepare:dev && pnpm run winClean && webpack --progress --env config=dev", @@ -22,10 +22,11 @@ "test:e2e": "playwright test", "test:e2e:ui": "playwright test --ui", "test:unit": "vitest", - "pub": "node build/release.js", + "pub": "pnpx tsx build/release.ts", "prepare": "husky", "preinstall": "npx only-allow pnpm", - "setup-secrets": "node build/setup-github-secrets.js" + "analyze:imports": "pnpx tsx build/analyze-imports.ts", + "analyze:dependencies": "pnpx tsx build/analyze-dependencies.ts" }, "dependencies": { "@coinbase/cbpay-js": "^1.10.0", @@ -155,6 +156,7 @@ "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", "@types/react-router-dom": "^5.3.3", + "@types/webpack": "^5.28.5", "@typescript-eslint/eslint-plugin": "^8.15.0", "@typescript-eslint/parser": "^8.15.0", "@welldone-software/why-did-you-render": "^6.2.3", @@ -198,7 +200,7 @@ "style-loader": "^2.0.0", "ts-import-plugin": "^1.6.7", "ts-loader": "^9.5.1", - "tsconfig-paths-webpack-plugin": "^3.5.2", + "tsconfig-paths-webpack-plugin": "^4.2.0", "typescript": "^5.7.2", "typescript-eslint": "^8.15.0", "typescript-transform-paths": "^3.5.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3797ccb0..86aa052f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -384,6 +384,9 @@ importers: '@types/react-router-dom': specifier: ^5.3.3 version: 5.3.3 + '@types/webpack': + specifier: ^5.28.5 + version: 5.28.5(webpack-cli@4.10.0) '@typescript-eslint/eslint-plugin': specifier: ^8.15.0 version: 8.17.0(@typescript-eslint/parser@8.17.0(eslint@9.16.0(jiti@2.4.1))(typescript@5.7.2))(eslint@9.16.0(jiti@2.4.1))(typescript@5.7.2) @@ -514,8 +517,8 @@ importers: specifier: ^9.5.1 version: 9.5.1(typescript@5.7.2)(webpack@5.97.1) tsconfig-paths-webpack-plugin: - specifier: ^3.5.2 - version: 3.5.2 + specifier: ^4.2.0 + version: 4.2.0 typescript: specifier: ^5.7.2 version: 5.7.2 @@ -3166,6 +3169,9 @@ packages: '@types/w3c-web-hid@1.0.6': resolution: {integrity: sha512-IWyssXmRDo6K7s31dxf+U+x/XUWuVsl9qUIYbJmpUHPcTv/COfBCKw/F0smI45+gPV34brjyP30BFcIsHgYWLA==} + '@types/webpack@5.28.5': + resolution: {integrity: sha512-wR87cgvxj3p6D0Crt1r5avwqffqPXUkNlnQ1mjU93G7gCuFjufZR4I6j8cz5g1F1tTYpfOOFvly+cmIQwL9wvw==} + '@types/ws@8.5.3': resolution: {integrity: sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==} @@ -9284,12 +9290,17 @@ packages: ts-toolbelt@9.6.0: resolution: {integrity: sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==} - tsconfig-paths-webpack-plugin@3.5.2: - resolution: {integrity: sha512-EhnfjHbzm5IYI9YPNVIxx1moxMI4bpHD2e0zTXeDNQcwjjRaGepP7IhTHJkyDBG0CAOoxRfe7jCG630Ou+C6Pw==} + tsconfig-paths-webpack-plugin@4.2.0: + resolution: {integrity: sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==} + engines: {node: '>=10.13.0'} tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + tsconfig-paths@4.2.0: + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} + tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} @@ -13800,6 +13811,17 @@ snapshots: '@types/w3c-web-hid@1.0.6': {} + '@types/webpack@5.28.5(webpack-cli@4.10.0)': + dependencies: + '@types/node': 22.10.1 + tapable: 2.2.1 + webpack: 5.97.1(webpack-cli@4.10.0) + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + - webpack-cli + '@types/ws@8.5.3': dependencies: '@types/node': 22.10.1 @@ -21856,11 +21878,12 @@ snapshots: ts-toolbelt@9.6.0: {} - tsconfig-paths-webpack-plugin@3.5.2: + tsconfig-paths-webpack-plugin@4.2.0: dependencies: chalk: 4.1.2 enhanced-resolve: 5.17.1 - tsconfig-paths: 3.15.0 + tapable: 2.2.1 + tsconfig-paths: 4.2.0 tsconfig-paths@3.15.0: dependencies: @@ -21869,6 +21892,12 @@ snapshots: minimist: 1.2.8 strip-bom: 3.0.0 + tsconfig-paths@4.2.0: + dependencies: + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 + tslib@1.14.1: {} tslib@2.7.0: {} diff --git a/src/background/service/googleSafeHost.ts b/src/background/service/googleSafeHost.ts index 36e38ca1..3b00cc11 100644 --- a/src/background/service/googleSafeHost.ts +++ b/src/background/service/googleSafeHost.ts @@ -48,7 +48,7 @@ class GoogleSafeHost { return []; } const hostList = hosts.map((host) => new URL(host).host); - const unique = Array.from(new Set(hostList)).map((host) => { url: host }); + const unique = Array.from(new Set(hostList)).map((host): GoogleHostModel => ({ url: host })); const { data } = await this.sendRequest(unique); this.setExpiry(); if (data.matches && data.matches > 0) { diff --git a/tsconfig.json b/tsconfig.json index 131e6730..90f2b124 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -37,5 +37,5 @@ "outDir": "./dist" }, "exclude": ["./node_modules"], - "include": ["src", "__tests__"] + "include": ["src", "__tests__", "build", "webpack.config.ts"] } diff --git a/webpack.config.js b/webpack.config.ts similarity index 58% rename from webpack.config.js rename to webpack.config.ts index 8e0e241d..ff47c68d 100644 --- a/webpack.config.js +++ b/webpack.config.ts @@ -1,11 +1,13 @@ -const webpack = require('webpack'); -const webpackMerge = require('webpack-merge'); +import webpack from 'webpack'; +import { merge } from 'webpack-merge'; -const commonConfig = require('./build/webpack.common.config'); +import commonConfig from './build/webpack.common.config'; +import dev from './build/webpack.dev.config'; +import pro from './build/webpack.pro.config'; -const configs = { - dev: require('./build/webpack.dev.config'), - pro: require('./build/webpack.pro.config'), +const configs: Record<'dev' | 'pro' | 'none', webpack.Configuration> = { + dev, + pro, none: { mode: 'development', devtool: false, @@ -36,12 +38,15 @@ const configs = { }, }; -const config = (env, argv) => { +const config = ( + env: { config: 'dev' | 'pro' | 'none' }, + _argv: unknown = {} +): webpack.Configuration => { if (env.config) { - return webpackMerge.merge(commonConfig(env), configs[env.config]); + return merge(commonConfig(env), configs[env.config]); } return commonConfig(env); }; -module.exports = config; +export default config;