@arcpkg/initiator 0.0.1-beta.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +524 -0
- package/config.ts +153 -0
- package/index.ts +23 -0
- package/intl.ts +366 -0
- package/js/config.js +110 -0
- package/js/index.js +17 -0
- package/js/intl.js +287 -0
- package/js/native-routing.js +696 -0
- package/js/provider.js +303 -0
- package/js/routing.js +533 -0
- package/native-routing.ts +871 -0
- package/package.json +21 -0
- package/provider.ts +403 -0
- package/routing.ts +691 -0
- package/tsconfig.json +27 -0
package/js/provider.js
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { Pajo } from '@arcpkg/pajo';
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = path.dirname(__filename);
|
|
7
|
+
export class ProviderGenerator {
|
|
8
|
+
config;
|
|
9
|
+
providers = [];
|
|
10
|
+
modules = new Set();
|
|
11
|
+
constructor(config = {}, dirname = __dirname) {
|
|
12
|
+
this.config = {
|
|
13
|
+
srcDir: config.srcDir || Pajo.join(dirname, 'src') || '',
|
|
14
|
+
modulesDir: config.modulesDir || Pajo.join(dirname, 'src\\modules') || '',
|
|
15
|
+
providersDir: config.providersDir || Pajo.join(dirname, 'src\\providers') || '',
|
|
16
|
+
outputFile: config.outputFile || Pajo.join(dirname, 'src\\auto-provider.tsx') || '',
|
|
17
|
+
globalProvidersDir: config.globalProvidersDir || Pajo.join(dirname, 'src\\providers') || ''
|
|
18
|
+
};
|
|
19
|
+
console.log(`--> ProviderGenerator config:: `, this.config);
|
|
20
|
+
}
|
|
21
|
+
async generate() {
|
|
22
|
+
console.log('🔍 Scanning for provider files...');
|
|
23
|
+
await this.findProviderFiles();
|
|
24
|
+
await this.generateAutoProviderFile();
|
|
25
|
+
console.log(`✅ Generated ${this.config.outputFile} with ${this.providers.length} providers`);
|
|
26
|
+
console.log(`📦 Found modules with providers: ${Array.from(this.modules).join(', ')}`);
|
|
27
|
+
}
|
|
28
|
+
async findProviderFiles() {
|
|
29
|
+
this.providers = [];
|
|
30
|
+
await this.scanDirectoryForProviders(this.config.globalProvidersDir, undefined, true);
|
|
31
|
+
if (fs.existsSync(this.config.modulesDir)) {
|
|
32
|
+
const moduleDirs = fs.readdirSync(this.config.modulesDir, { withFileTypes: true })
|
|
33
|
+
.filter(dirent => dirent.isDirectory())
|
|
34
|
+
.map(dirent => dirent.name);
|
|
35
|
+
for (const moduleName of moduleDirs) {
|
|
36
|
+
const moduleProvidersDir = path.join(this.config.modulesDir, moduleName, 'providers');
|
|
37
|
+
if (fs.existsSync(moduleProvidersDir)) {
|
|
38
|
+
this.modules.add(moduleName);
|
|
39
|
+
await this.scanDirectoryForProviders(moduleProvidersDir, moduleName, false);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async scanDirectoryForProviders(dir, moduleName, isGlobal = false) {
|
|
45
|
+
try {
|
|
46
|
+
if (!fs.existsSync(dir)) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const items = fs.readdirSync(dir, { withFileTypes: true });
|
|
50
|
+
for (const item of items) {
|
|
51
|
+
const fullPath = path.join(dir, item.name);
|
|
52
|
+
if (item.name === 'node_modules' ||
|
|
53
|
+
item.name === 'dist' ||
|
|
54
|
+
item.name === 'build' ||
|
|
55
|
+
item.name.startsWith('.') ||
|
|
56
|
+
item.name === 'index.ts' ||
|
|
57
|
+
item.name === 'index.tsx' ||
|
|
58
|
+
item.name === 'auto-provider.tsx') {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (item.isDirectory()) {
|
|
62
|
+
await this.scanDirectoryForProviders(fullPath, moduleName, isGlobal);
|
|
63
|
+
}
|
|
64
|
+
else if (item.isFile()) {
|
|
65
|
+
const ext = path.extname(item.name).toLowerCase();
|
|
66
|
+
if (['.tsx', '.jsx', '.ts', '.js'].includes(ext)) {
|
|
67
|
+
const fileName = path.basename(item.name, ext);
|
|
68
|
+
if (fileName.includes('Provider') || this.isLikelyProviderFile(fullPath)) {
|
|
69
|
+
await this.processProviderFile(fullPath, moduleName, isGlobal);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
console.error(`Error scanning directory ${dir}:`, error);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async processProviderFile(filePath, moduleName, isGlobal = false) {
|
|
80
|
+
try {
|
|
81
|
+
const componentName = this.extractComponentName(filePath, moduleName, isGlobal);
|
|
82
|
+
const providerType = this.determineProviderType(filePath, componentName);
|
|
83
|
+
const priority = this.determineProviderPriority(providerType, componentName, isGlobal);
|
|
84
|
+
const providerInfo = {
|
|
85
|
+
filePath,
|
|
86
|
+
componentName,
|
|
87
|
+
moduleName,
|
|
88
|
+
priority,
|
|
89
|
+
isGlobal,
|
|
90
|
+
providerType
|
|
91
|
+
};
|
|
92
|
+
this.providers.push(providerInfo);
|
|
93
|
+
console.log(`📦 Found provider: ${componentName} (${providerType}, priority: ${priority})`);
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
console.error(`Error processing provider file ${filePath}:`, error);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
extractComponentName(filePath, moduleName, isGlobal) {
|
|
100
|
+
const fileName = path.basename(filePath, path.extname(filePath));
|
|
101
|
+
let componentName = fileName
|
|
102
|
+
.replace(/\.provider$/i, '')
|
|
103
|
+
.replace(/Provider$/i, '')
|
|
104
|
+
.replace(/\.context$/i, '');
|
|
105
|
+
if (!componentName.match(/^[A-Z]/)) {
|
|
106
|
+
componentName = this.toPascalCase(componentName);
|
|
107
|
+
}
|
|
108
|
+
if (!componentName.endsWith('Provider')) {
|
|
109
|
+
componentName += 'Provider';
|
|
110
|
+
}
|
|
111
|
+
if (moduleName && isGlobal === false) {
|
|
112
|
+
const modulePrefix = this.toPascalCase(moduleName);
|
|
113
|
+
if (!componentName.startsWith(modulePrefix)) {
|
|
114
|
+
componentName = modulePrefix + componentName;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return componentName;
|
|
118
|
+
}
|
|
119
|
+
toPascalCase(str) {
|
|
120
|
+
if (!str)
|
|
121
|
+
return '';
|
|
122
|
+
return str
|
|
123
|
+
.split(/[-_]/)
|
|
124
|
+
.map(part => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
|
|
125
|
+
.join('');
|
|
126
|
+
}
|
|
127
|
+
isLikelyProviderFile(filePath) {
|
|
128
|
+
try {
|
|
129
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
130
|
+
const providerPatterns = [
|
|
131
|
+
/createContext/i,
|
|
132
|
+
/Context\.Provider/i,
|
|
133
|
+
/Provider.*children/i,
|
|
134
|
+
/<Provider/i,
|
|
135
|
+
/export.*Provider/i
|
|
136
|
+
];
|
|
137
|
+
return providerPatterns.some(pattern => pattern.test(content));
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
determineProviderType(filePath, componentName) {
|
|
144
|
+
try {
|
|
145
|
+
const content = fs.readFileSync(filePath, 'utf-8').toLowerCase();
|
|
146
|
+
const fileName = path.basename(filePath).toLowerCase();
|
|
147
|
+
if (content.includes('redux') || content.includes('store') || fileName.includes('redux') || fileName.includes('store')) {
|
|
148
|
+
return 'redux';
|
|
149
|
+
}
|
|
150
|
+
if (content.includes('react-router') || content.includes('browserrouter') || fileName.includes('router')) {
|
|
151
|
+
return 'router';
|
|
152
|
+
}
|
|
153
|
+
if (content.includes('theme') || content.includes('themeprovider') || fileName.includes('theme')) {
|
|
154
|
+
return 'theme';
|
|
155
|
+
}
|
|
156
|
+
if (content.includes('createcontext') || content.includes('context.provider') || fileName.includes('context')) {
|
|
157
|
+
return 'context';
|
|
158
|
+
}
|
|
159
|
+
return 'other';
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
return 'other';
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
determineProviderPriority(type, componentName, isGlobal) {
|
|
166
|
+
const basePriority = isGlobal ? 100 : 200;
|
|
167
|
+
const typePriority = {
|
|
168
|
+
'router': 10,
|
|
169
|
+
'redux': 20,
|
|
170
|
+
'context': 30,
|
|
171
|
+
'theme': 40,
|
|
172
|
+
'other': 50
|
|
173
|
+
};
|
|
174
|
+
if (componentName.includes('Router') || componentName.includes('BrowserRouter')) {
|
|
175
|
+
return basePriority + 5;
|
|
176
|
+
}
|
|
177
|
+
if (componentName.includes('Error') || componentName.includes('Boundary')) {
|
|
178
|
+
return basePriority + 100;
|
|
179
|
+
}
|
|
180
|
+
if (componentName.includes('Toast') || componentName.includes('Notification')) {
|
|
181
|
+
return basePriority + 90;
|
|
182
|
+
}
|
|
183
|
+
return basePriority + (typePriority[type] || 50);
|
|
184
|
+
}
|
|
185
|
+
async generateAutoProviderFile() {
|
|
186
|
+
const outputDir = path.dirname(this.config.outputFile);
|
|
187
|
+
if (!fs.existsSync(outputDir)) {
|
|
188
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
189
|
+
}
|
|
190
|
+
const content = this.generateFileContent();
|
|
191
|
+
fs.writeFileSync(this.config.outputFile, content, 'utf-8');
|
|
192
|
+
}
|
|
193
|
+
getRelativeImportPath(targetPath) {
|
|
194
|
+
const outputDir = path.dirname(this.config.outputFile);
|
|
195
|
+
const relative = path.relative(outputDir, targetPath);
|
|
196
|
+
let importPath = relative.replace(/\\/g, '/');
|
|
197
|
+
if (!importPath.startsWith('.') && !importPath.startsWith('/')) {
|
|
198
|
+
importPath = './' + importPath;
|
|
199
|
+
}
|
|
200
|
+
importPath = importPath.replace(/\.(tsx|jsx|ts|js)$/, '');
|
|
201
|
+
return importPath;
|
|
202
|
+
}
|
|
203
|
+
generateFileContent() {
|
|
204
|
+
const sortedProviders = [...this.providers].sort((a, b) => a.priority - b.priority);
|
|
205
|
+
const providersByModule = {};
|
|
206
|
+
const globalProviders = [];
|
|
207
|
+
sortedProviders.forEach(provider => {
|
|
208
|
+
if (provider.isGlobal || !provider.moduleName) {
|
|
209
|
+
globalProviders.push(provider);
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
if (!providersByModule[provider.moduleName]) {
|
|
213
|
+
providersByModule[provider.moduleName] = [];
|
|
214
|
+
}
|
|
215
|
+
providersByModule[provider.moduleName].push(provider);
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
const imports = [
|
|
219
|
+
`import React, { lazy } from 'react';`,
|
|
220
|
+
``,
|
|
221
|
+
``
|
|
222
|
+
];
|
|
223
|
+
const allProviders = [...globalProviders, ...Object.values(providersByModule).flat()];
|
|
224
|
+
allProviders.forEach(provider => {
|
|
225
|
+
const importPath = this.getRelativeImportPath(provider.filePath);
|
|
226
|
+
const actualExportName = this.tryGetExportName(provider.filePath, provider.componentName);
|
|
227
|
+
imports.push(`const ${provider.componentName} = lazy(() => import('${importPath}').then(module => ({ default: module.${actualExportName} || module.default || module })));`);
|
|
228
|
+
});
|
|
229
|
+
imports.push(``);
|
|
230
|
+
const moduleComponents = [];
|
|
231
|
+
Object.entries(providersByModule).forEach(([moduleName, moduleProviders]) => {
|
|
232
|
+
const pascalModuleName = this.toPascalCase(moduleName);
|
|
233
|
+
const componentName = `${pascalModuleName}Providers`;
|
|
234
|
+
const sortedModuleProviders = moduleProviders.sort((a, b) => a.priority - b.priority);
|
|
235
|
+
let nestedJSX = '{children}';
|
|
236
|
+
sortedModuleProviders.forEach(provider => {
|
|
237
|
+
nestedJSX = `<${provider.componentName}>\n ${nestedJSX}\n </${provider.componentName}>`;
|
|
238
|
+
});
|
|
239
|
+
moduleComponents.push(`const ${componentName} = ({ children }: { children: any }) => (<>${nestedJSX}</>);`);
|
|
240
|
+
moduleComponents.push(``);
|
|
241
|
+
});
|
|
242
|
+
const sortedGlobalProviders = globalProviders.sort((a, b) => a.priority - b.priority);
|
|
243
|
+
let globalNestedJSX = '{children}';
|
|
244
|
+
sortedGlobalProviders.forEach(provider => {
|
|
245
|
+
globalNestedJSX = `<${provider.componentName}>\n ${globalNestedJSX}\n </${provider.componentName}>`;
|
|
246
|
+
});
|
|
247
|
+
moduleComponents.push(`const GlobalProviders = ({ children }: { children: any }) => (<>${globalNestedJSX}</>);`);
|
|
248
|
+
moduleComponents.push(``);
|
|
249
|
+
const allModuleNames = Object.keys(providersByModule);
|
|
250
|
+
let mainNestedJSX = '{children}';
|
|
251
|
+
allModuleNames.sort().forEach(moduleName => {
|
|
252
|
+
const pascalModuleName = this.toPascalCase(moduleName);
|
|
253
|
+
const componentName = `${pascalModuleName}Providers`;
|
|
254
|
+
mainNestedJSX = `<${componentName}>\n ${mainNestedJSX}\n </${componentName}>`;
|
|
255
|
+
});
|
|
256
|
+
mainNestedJSX = `<GlobalProviders>\n ${mainNestedJSX}\n</GlobalProviders>`;
|
|
257
|
+
moduleComponents.push(`export const AppProvider = ({ children }: { children: any }) => (<>${mainNestedJSX}</>);`);
|
|
258
|
+
moduleComponents.push(``);
|
|
259
|
+
moduleComponents.push(`export default AppProvider;`);
|
|
260
|
+
return [
|
|
261
|
+
...imports,
|
|
262
|
+
...moduleComponents
|
|
263
|
+
].join('\n');
|
|
264
|
+
}
|
|
265
|
+
tryGetExportName(filePath, defaultName) {
|
|
266
|
+
try {
|
|
267
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
268
|
+
const exportRegex = /export\s+(?:const|let|var|function|class)\s+(\w+)/g;
|
|
269
|
+
const exports = [];
|
|
270
|
+
let match;
|
|
271
|
+
while ((match = exportRegex.exec(content)) !== null) {
|
|
272
|
+
exports.push(match[1]);
|
|
273
|
+
}
|
|
274
|
+
const defaultExportRegex = /export\s+default\s+(\w+)/;
|
|
275
|
+
const defaultMatch = defaultExportRegex.exec(content);
|
|
276
|
+
if (exports.includes(defaultName)) {
|
|
277
|
+
return defaultName;
|
|
278
|
+
}
|
|
279
|
+
else if (defaultMatch) {
|
|
280
|
+
return defaultMatch[1];
|
|
281
|
+
}
|
|
282
|
+
else if (exports.length > 0) {
|
|
283
|
+
return exports[0];
|
|
284
|
+
}
|
|
285
|
+
return 'default';
|
|
286
|
+
}
|
|
287
|
+
catch (error) {
|
|
288
|
+
return 'default';
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
async function ProviderInitiator(dirname = __dirname) {
|
|
293
|
+
const config = {};
|
|
294
|
+
const generator = new ProviderGenerator(config, dirname);
|
|
295
|
+
try {
|
|
296
|
+
await generator.generate();
|
|
297
|
+
console.log('🎉 Provider generation completed successfully!');
|
|
298
|
+
}
|
|
299
|
+
catch (err) {
|
|
300
|
+
console.error('❌ Failed to generate providers: ', err);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
export default ProviderInitiator;
|