@julien-lin/universal-pwa-core 1.3.14 → 1.3.16
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/dist/index.cjs +508 -276
- package/dist/index.d.cts +49 -1
- package/dist/index.d.ts +49 -1
- package/dist/index.js +502 -274
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -102,9 +102,11 @@ __export(index_exports, {
|
|
|
102
102
|
ConfigLoadError: () => ConfigLoadError,
|
|
103
103
|
ConfigValidationError: () => ConfigValidationError,
|
|
104
104
|
DEFAULT_CONFIG: () => DEFAULT_CONFIG,
|
|
105
|
+
DEFAULT_INJECTION_EXTENSIONS: () => DEFAULT_INJECTION_EXTENSIONS,
|
|
105
106
|
DefaultBackendIntegrationFactory: () => DefaultBackendIntegrationFactory,
|
|
106
107
|
DjangoIntegration: () => DjangoIntegration,
|
|
107
108
|
FlaskIntegration: () => FlaskIntegration,
|
|
109
|
+
INJECTION_EXTENSIONS_BY_PROJECT_TYPE: () => INJECTION_EXTENSIONS_BY_PROJECT_TYPE,
|
|
108
110
|
IconConfigSchema: () => IconConfigSchema,
|
|
109
111
|
InjectionConfigSchema: () => InjectionConfigSchema,
|
|
110
112
|
LaravelIntegration: () => LaravelIntegration,
|
|
@@ -120,6 +122,7 @@ __export(index_exports, {
|
|
|
120
122
|
SymfonyIntegration: () => SymfonyIntegration,
|
|
121
123
|
TelemetryCollector: () => TelemetryCollector,
|
|
122
124
|
UniversalPWAConfigSchema: () => UniversalPWAConfigSchema,
|
|
125
|
+
WORDPRESS_INJECTION_PATTERNS: () => WORDPRESS_INJECTION_PATTERNS,
|
|
123
126
|
buildDependencyGraph: () => buildDependencyGraph,
|
|
124
127
|
checkHttps: () => checkHttps,
|
|
125
128
|
checkProjectHttps: () => checkProjectHttps,
|
|
@@ -176,6 +179,7 @@ __export(index_exports, {
|
|
|
176
179
|
injectMetaTagsInFile: () => injectMetaTagsInFile,
|
|
177
180
|
injectMetaTagsInFilesBatch: () => injectMetaTagsInFilesBatch,
|
|
178
181
|
loadConfig: () => loadConfig,
|
|
182
|
+
mapBackendManifestVarsToOptions: () => mapBackendManifestVarsToOptions,
|
|
179
183
|
optimizeImage: () => optimizeImage,
|
|
180
184
|
optimizeProject: () => optimizeProject,
|
|
181
185
|
optimizeProjectImages: () => optimizeProjectImages,
|
|
@@ -1067,25 +1071,25 @@ function createResult(framework, confidence, indicators, version = null, configu
|
|
|
1067
1071
|
configuration: finalConfig
|
|
1068
1072
|
};
|
|
1069
1073
|
}
|
|
1074
|
+
function hasFileAndDirectory(projectPath, file, dir) {
|
|
1075
|
+
return (0, import_fs.existsSync)((0, import_path.join)(projectPath, file)) && (0, import_fs.existsSync)((0, import_path.join)(projectPath, dir));
|
|
1076
|
+
}
|
|
1070
1077
|
function detectFramework(projectPath) {
|
|
1071
1078
|
const indicators = [];
|
|
1072
1079
|
let framework = null;
|
|
1073
1080
|
let confidence = "low";
|
|
1074
1081
|
const projectConfig = detectProjectConfiguration(projectPath);
|
|
1075
|
-
if ((
|
|
1076
|
-
indicators.push("wp-config.php");
|
|
1077
|
-
if ((0, import_fs.existsSync)((0, import_path.join)(projectPath, "wp-content"))) {
|
|
1078
|
-
indicators.push("wp-content/");
|
|
1079
|
-
|
|
1080
|
-
indicators.push("wp-content/plugins/woocommerce/");
|
|
1081
|
-
framework = "woocommerce";
|
|
1082
|
-
confidence = "high";
|
|
1083
|
-
return createResult(framework, confidence, indicators, null, projectConfig);
|
|
1084
|
-
}
|
|
1085
|
-
framework = "wordpress";
|
|
1082
|
+
if (hasFileAndDirectory(projectPath, "wp-config.php", "wp-content")) {
|
|
1083
|
+
indicators.push("wp-config.php", "wp-content/");
|
|
1084
|
+
if ((0, import_fs.existsSync)((0, import_path.join)(projectPath, "wp-content", "plugins", "woocommerce"))) {
|
|
1085
|
+
indicators.push("wp-content/plugins/woocommerce/");
|
|
1086
|
+
framework = "woocommerce";
|
|
1086
1087
|
confidence = "high";
|
|
1087
1088
|
return createResult(framework, confidence, indicators, null, projectConfig);
|
|
1088
1089
|
}
|
|
1090
|
+
framework = "wordpress";
|
|
1091
|
+
confidence = "high";
|
|
1092
|
+
return createResult(framework, confidence, indicators, null, projectConfig);
|
|
1089
1093
|
}
|
|
1090
1094
|
if ((0, import_fs.existsSync)((0, import_path.join)(projectPath, "sites")) && (0, import_fs.existsSync)((0, import_path.join)(projectPath, "modules"))) {
|
|
1091
1095
|
indicators.push("sites/ and modules/ (Drupal)");
|
|
@@ -1096,14 +1100,11 @@ function detectFramework(projectPath) {
|
|
|
1096
1100
|
return createResult(framework, confidence, indicators, null, projectConfig);
|
|
1097
1101
|
}
|
|
1098
1102
|
}
|
|
1099
|
-
if ((
|
|
1100
|
-
indicators.push("configuration.php (Joomla)");
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
confidence = "high";
|
|
1105
|
-
return createResult(framework, confidence, indicators, null, projectConfig);
|
|
1106
|
-
}
|
|
1103
|
+
if (hasFileAndDirectory(projectPath, "configuration.php", "administrator")) {
|
|
1104
|
+
indicators.push("configuration.php (Joomla)", "administrator/");
|
|
1105
|
+
framework = "joomla";
|
|
1106
|
+
confidence = "high";
|
|
1107
|
+
return createResult(framework, confidence, indicators, null, projectConfig);
|
|
1107
1108
|
}
|
|
1108
1109
|
if ((0, import_fs.existsSync)((0, import_path.join)(projectPath, "theme.liquid")) || (0, import_fs.existsSync)((0, import_path.join)(projectPath, "config", "settings_schema.json"))) {
|
|
1109
1110
|
indicators.push("theme.liquid or config/settings_schema.json (Shopify)");
|
|
@@ -2580,6 +2581,22 @@ var ManifestSchema = import_zod.z.object({
|
|
|
2580
2581
|
prefer_related_applications: import_zod.z.boolean().optional(),
|
|
2581
2582
|
related_applications: import_zod.z.array(import_zod.z.unknown()).optional()
|
|
2582
2583
|
});
|
|
2584
|
+
function mapBackendManifestVarsToOptions(vars) {
|
|
2585
|
+
const out = {};
|
|
2586
|
+
if (typeof vars.name === "string") out.name = vars.name;
|
|
2587
|
+
if (typeof vars.short_name === "string") out.shortName = vars.short_name;
|
|
2588
|
+
if (typeof vars.description === "string") out.description = vars.description;
|
|
2589
|
+
if (typeof vars.start_url === "string") out.startUrl = vars.start_url;
|
|
2590
|
+
if (typeof vars.scope === "string") out.scope = vars.scope;
|
|
2591
|
+
if (typeof vars.display === "string")
|
|
2592
|
+
out.display = vars.display;
|
|
2593
|
+
if (typeof vars.theme_color === "string") out.themeColor = vars.theme_color;
|
|
2594
|
+
if (typeof vars.background_color === "string")
|
|
2595
|
+
out.backgroundColor = vars.background_color;
|
|
2596
|
+
if (typeof vars.orientation === "string")
|
|
2597
|
+
out.orientation = vars.orientation;
|
|
2598
|
+
return out;
|
|
2599
|
+
}
|
|
2583
2600
|
function generateManifest(options) {
|
|
2584
2601
|
let shortName = "PWA";
|
|
2585
2602
|
if (options.shortName && typeof options.shortName === "string" && options.shortName.trim().length > 0) {
|
|
@@ -4660,7 +4677,9 @@ async function generateServiceWorker(options) {
|
|
|
4660
4677
|
const { patterns: validatedPatterns, warnings: patternWarnings } = validateAndLimitPrecachePatterns(globPatterns, framework);
|
|
4661
4678
|
(0, import_fs10.mkdirSync)(outputDir, { recursive: true });
|
|
4662
4679
|
const finalTemplateType = templateType ?? (0, import_universal_pwa_templates.determineTemplateType)(architecture, framework ?? null);
|
|
4663
|
-
const template = (0, import_universal_pwa_templates.getServiceWorkerTemplate)(
|
|
4680
|
+
const template = (0, import_universal_pwa_templates.getServiceWorkerTemplate)(
|
|
4681
|
+
finalTemplateType
|
|
4682
|
+
);
|
|
4664
4683
|
const swSrcPath = (0, import_path9.join)(outputDir, "sw-src.js");
|
|
4665
4684
|
(0, import_fs10.writeFileSync)(swSrcPath, template.content, "utf-8");
|
|
4666
4685
|
const swDestPath = (0, import_path9.join)(outputDir, swDest);
|
|
@@ -4756,7 +4775,7 @@ async function generateSimpleServiceWorker(options) {
|
|
|
4756
4775
|
count: result.count,
|
|
4757
4776
|
size: result.size,
|
|
4758
4777
|
warnings: [...result.warnings ?? [], ...patternWarnings],
|
|
4759
|
-
filePaths:
|
|
4778
|
+
filePaths: result.filePaths ?? []
|
|
4760
4779
|
};
|
|
4761
4780
|
} catch (err) {
|
|
4762
4781
|
const message = getErrorMessage(err);
|
|
@@ -4777,7 +4796,9 @@ async function generateServiceWorkerFromConfig(config, options) {
|
|
|
4777
4796
|
swDest = "sw.js"
|
|
4778
4797
|
} = options;
|
|
4779
4798
|
const finalTemplateType = templateType ?? (0, import_universal_pwa_templates.determineTemplateType)(architecture, framework ?? null);
|
|
4780
|
-
const template = (0, import_universal_pwa_templates.getServiceWorkerTemplate)(
|
|
4799
|
+
const template = (0, import_universal_pwa_templates.getServiceWorkerTemplate)(
|
|
4800
|
+
finalTemplateType
|
|
4801
|
+
);
|
|
4781
4802
|
const swSrcPath = (0, import_path9.join)(outputDir, "sw-src.js");
|
|
4782
4803
|
(0, import_fs10.writeFileSync)(swSrcPath, template.content, "utf-8");
|
|
4783
4804
|
const swDestPath = (0, import_path9.join)(outputDir, swDest);
|
|
@@ -6196,12 +6217,32 @@ var ManifestConfigSchema = import_zod2.z.object({
|
|
|
6196
6217
|
/** Orientation */
|
|
6197
6218
|
orientation: import_zod2.z.enum(["any", "natural", "landscape", "portrait", "portrait-primary", "portrait-secondary", "landscape-primary", "landscape-secondary"]).optional()
|
|
6198
6219
|
});
|
|
6220
|
+
var DEFAULT_INJECTION_EXTENSIONS = [
|
|
6221
|
+
"html",
|
|
6222
|
+
"twig",
|
|
6223
|
+
"html.twig",
|
|
6224
|
+
"blade.php",
|
|
6225
|
+
"jinja2",
|
|
6226
|
+
"j2",
|
|
6227
|
+
"html.j2"
|
|
6228
|
+
];
|
|
6229
|
+
var INJECTION_EXTENSIONS_BY_PROJECT_TYPE = {
|
|
6230
|
+
php: ["html", "twig", "html.twig", "blade.php"],
|
|
6231
|
+
python: ["html", "jinja2", "j2", "html.j2"],
|
|
6232
|
+
static: ["html"]
|
|
6233
|
+
};
|
|
6234
|
+
var WORDPRESS_INJECTION_PATTERNS = [
|
|
6235
|
+
"**/wp-content/themes/**/header.php",
|
|
6236
|
+
"**/wp-content/themes/**/footer.php"
|
|
6237
|
+
];
|
|
6199
6238
|
var InjectionConfigSchema = import_zod2.z.object({
|
|
6200
6239
|
/** Inject meta tags */
|
|
6201
6240
|
inject: import_zod2.z.boolean().default(true),
|
|
6202
6241
|
/** Maximum number of HTML files to process */
|
|
6203
6242
|
maxFiles: import_zod2.z.number().positive().optional(),
|
|
6204
|
-
/** HTML
|
|
6243
|
+
/** File extensions for HTML/template injection (e.g. html, twig, blade.php, j2). Used to build glob pattern. */
|
|
6244
|
+
extensions: import_zod2.z.array(import_zod2.z.string()).optional(),
|
|
6245
|
+
/** HTML file patterns to include (overrides extensions if set) */
|
|
6205
6246
|
include: import_zod2.z.array(import_zod2.z.string()).optional(),
|
|
6206
6247
|
/** HTML file patterns to exclude */
|
|
6207
6248
|
exclude: import_zod2.z.array(import_zod2.z.string()).optional()
|
|
@@ -6918,15 +6959,49 @@ var BaseBackendIntegration = class {
|
|
|
6918
6959
|
};
|
|
6919
6960
|
|
|
6920
6961
|
// src/backends/laravel.ts
|
|
6962
|
+
var import_node_fs8 = require("fs");
|
|
6963
|
+
var import_node_path8 = require("path");
|
|
6964
|
+
|
|
6965
|
+
// src/backends/spa-detector.ts
|
|
6921
6966
|
var import_node_fs7 = require("fs");
|
|
6922
6967
|
var import_node_path7 = require("path");
|
|
6968
|
+
var DEFAULT_VITE_INCLUDES = ["vue", "react"];
|
|
6969
|
+
var DEFAULT_PACKAGE_KEYS = ["vue", "react"];
|
|
6970
|
+
function detectSPAFromViteAndPackage(projectRoot, options = {}) {
|
|
6971
|
+
const viteIncludes = options.viteIncludes ?? DEFAULT_VITE_INCLUDES;
|
|
6972
|
+
const packageKeys = options.packageKeys ?? DEFAULT_PACKAGE_KEYS;
|
|
6973
|
+
try {
|
|
6974
|
+
const vitePath = (0, import_node_fs7.existsSync)((0, import_node_path7.join)(projectRoot, "vite.config.ts")) ? (0, import_node_path7.join)(projectRoot, "vite.config.ts") : (0, import_node_fs7.existsSync)((0, import_node_path7.join)(projectRoot, "vite.config.js")) ? (0, import_node_path7.join)(projectRoot, "vite.config.js") : (0, import_node_fs7.existsSync)((0, import_node_path7.join)(projectRoot, "vite.config.mjs")) ? (0, import_node_path7.join)(projectRoot, "vite.config.mjs") : null;
|
|
6975
|
+
if (vitePath) {
|
|
6976
|
+
const viteContent = (0, import_node_fs7.readFileSync)(vitePath, "utf-8");
|
|
6977
|
+
if (viteIncludes.some((s) => viteContent.includes(s))) {
|
|
6978
|
+
return true;
|
|
6979
|
+
}
|
|
6980
|
+
}
|
|
6981
|
+
const packagePath = (0, import_node_path7.join)(projectRoot, "package.json");
|
|
6982
|
+
if ((0, import_node_fs7.existsSync)(packagePath)) {
|
|
6983
|
+
const content = (0, import_node_fs7.readFileSync)(packagePath, "utf-8");
|
|
6984
|
+
const pkg = JSON.parse(content);
|
|
6985
|
+
const deps = pkg.dependencies ?? {};
|
|
6986
|
+
const devDeps = pkg.devDependencies ?? {};
|
|
6987
|
+
if (packageKeys.some((key) => deps[key] !== void 0 || devDeps[key] !== void 0)) {
|
|
6988
|
+
return true;
|
|
6989
|
+
}
|
|
6990
|
+
}
|
|
6991
|
+
return false;
|
|
6992
|
+
} catch {
|
|
6993
|
+
return false;
|
|
6994
|
+
}
|
|
6995
|
+
}
|
|
6996
|
+
|
|
6997
|
+
// src/backends/laravel.ts
|
|
6923
6998
|
function detectLaravelVersion(projectRoot) {
|
|
6924
6999
|
try {
|
|
6925
|
-
const composerPath = (0,
|
|
6926
|
-
if (!(0,
|
|
7000
|
+
const composerPath = (0, import_node_path8.join)(projectRoot, "composer.json");
|
|
7001
|
+
if (!(0, import_node_fs8.existsSync)(composerPath)) {
|
|
6927
7002
|
return null;
|
|
6928
7003
|
}
|
|
6929
|
-
const content = (0,
|
|
7004
|
+
const content = (0, import_node_fs8.readFileSync)(composerPath, "utf-8");
|
|
6930
7005
|
const composer = JSON.parse(content);
|
|
6931
7006
|
const requires = composer.require;
|
|
6932
7007
|
const laravelVersion = requires?.["laravel/framework"];
|
|
@@ -6946,11 +7021,11 @@ function detectLaravelVersion(projectRoot) {
|
|
|
6946
7021
|
}
|
|
6947
7022
|
function hasLaravelDependency(projectRoot) {
|
|
6948
7023
|
try {
|
|
6949
|
-
const composerPath = (0,
|
|
6950
|
-
if (!(0,
|
|
7024
|
+
const composerPath = (0, import_node_path8.join)(projectRoot, "composer.json");
|
|
7025
|
+
if (!(0, import_node_fs8.existsSync)(composerPath)) {
|
|
6951
7026
|
return false;
|
|
6952
7027
|
}
|
|
6953
|
-
const content = (0,
|
|
7028
|
+
const content = (0, import_node_fs8.readFileSync)(composerPath, "utf-8");
|
|
6954
7029
|
const composer = JSON.parse(content);
|
|
6955
7030
|
const requires = composer.require;
|
|
6956
7031
|
return !!requires?.["laravel/framework"];
|
|
@@ -6960,11 +7035,11 @@ function hasLaravelDependency(projectRoot) {
|
|
|
6960
7035
|
}
|
|
6961
7036
|
function hasLumenDependency(projectRoot) {
|
|
6962
7037
|
try {
|
|
6963
|
-
const composerPath = (0,
|
|
6964
|
-
if (!(0,
|
|
7038
|
+
const composerPath = (0, import_node_path8.join)(projectRoot, "composer.json");
|
|
7039
|
+
if (!(0, import_node_fs8.existsSync)(composerPath)) {
|
|
6965
7040
|
return false;
|
|
6966
7041
|
}
|
|
6967
|
-
const content = (0,
|
|
7042
|
+
const content = (0, import_node_fs8.readFileSync)(composerPath, "utf-8");
|
|
6968
7043
|
const composer = JSON.parse(content);
|
|
6969
7044
|
const requires = composer.require;
|
|
6970
7045
|
return !!(requires?.["laravel/lumen-framework"] || requires?.["lumen/framework"]);
|
|
@@ -6973,33 +7048,18 @@ function hasLumenDependency(projectRoot) {
|
|
|
6973
7048
|
}
|
|
6974
7049
|
}
|
|
6975
7050
|
function detectSPAMode(projectRoot) {
|
|
6976
|
-
|
|
6977
|
-
|
|
6978
|
-
|
|
6979
|
-
|
|
6980
|
-
const viteContent = (0, import_node_fs7.readFileSync)(vitePath, "utf-8");
|
|
6981
|
-
return viteContent.includes("vue") || viteContent.includes("react") || viteContent.includes("inertia");
|
|
6982
|
-
}
|
|
6983
|
-
const packagePath = (0, import_node_path7.join)(projectRoot, "package.json");
|
|
6984
|
-
if ((0, import_node_fs7.existsSync)(packagePath)) {
|
|
6985
|
-
const content = (0, import_node_fs7.readFileSync)(packagePath, "utf-8");
|
|
6986
|
-
const pkg = JSON.parse(content);
|
|
6987
|
-
const dependencies = pkg.dependencies;
|
|
6988
|
-
const devDependencies = pkg.devDependencies;
|
|
6989
|
-
return !!(dependencies?.vue || dependencies?.react || dependencies?.["@inertiajs/inertia"] || devDependencies?.vue || devDependencies?.react || devDependencies?.["@inertiajs/inertia"]);
|
|
6990
|
-
}
|
|
6991
|
-
return false;
|
|
6992
|
-
} catch {
|
|
6993
|
-
return false;
|
|
6994
|
-
}
|
|
7051
|
+
return detectSPAFromViteAndPackage(projectRoot, {
|
|
7052
|
+
viteIncludes: ["vue", "react", "inertia"],
|
|
7053
|
+
packageKeys: ["vue", "react", "@inertiajs/inertia"]
|
|
7054
|
+
});
|
|
6995
7055
|
}
|
|
6996
7056
|
function detectLivewire(projectRoot) {
|
|
6997
7057
|
try {
|
|
6998
|
-
const composerPath = (0,
|
|
6999
|
-
if (!(0,
|
|
7058
|
+
const composerPath = (0, import_node_path8.join)(projectRoot, "composer.json");
|
|
7059
|
+
if (!(0, import_node_fs8.existsSync)(composerPath)) {
|
|
7000
7060
|
return false;
|
|
7001
7061
|
}
|
|
7002
|
-
const content = (0,
|
|
7062
|
+
const content = (0, import_node_fs8.readFileSync)(composerPath, "utf-8");
|
|
7003
7063
|
const composer = JSON.parse(content);
|
|
7004
7064
|
const requires = composer.require;
|
|
7005
7065
|
return !!(requires?.["livewire/livewire"] || requires?.livewire);
|
|
@@ -7007,6 +7067,36 @@ function detectLivewire(projectRoot) {
|
|
|
7007
7067
|
return false;
|
|
7008
7068
|
}
|
|
7009
7069
|
}
|
|
7070
|
+
function detectAlpine(projectRoot) {
|
|
7071
|
+
try {
|
|
7072
|
+
const packageJsonPath = (0, import_node_path8.join)(projectRoot, "package.json");
|
|
7073
|
+
if ((0, import_node_fs8.existsSync)(packageJsonPath)) {
|
|
7074
|
+
const content = (0, import_node_fs8.readFileSync)(packageJsonPath, "utf-8");
|
|
7075
|
+
const pkg = JSON.parse(content);
|
|
7076
|
+
const devDeps = pkg.devDependencies;
|
|
7077
|
+
const deps = pkg.dependencies;
|
|
7078
|
+
if (devDeps?.["alpinejs"] || deps?.["alpinejs"]) {
|
|
7079
|
+
return true;
|
|
7080
|
+
}
|
|
7081
|
+
}
|
|
7082
|
+
const resourcesPath = (0, import_node_path8.join)(projectRoot, "resources");
|
|
7083
|
+
if ((0, import_node_fs8.existsSync)(resourcesPath)) {
|
|
7084
|
+
try {
|
|
7085
|
+
const layoutFile = (0, import_node_path8.join)(resourcesPath, "views", "app.blade.php");
|
|
7086
|
+
if ((0, import_node_fs8.existsSync)(layoutFile)) {
|
|
7087
|
+
const content = (0, import_node_fs8.readFileSync)(layoutFile, "utf-8");
|
|
7088
|
+
if (/alpine|x-data|@click|x-show/i.test(content)) {
|
|
7089
|
+
return true;
|
|
7090
|
+
}
|
|
7091
|
+
}
|
|
7092
|
+
} catch {
|
|
7093
|
+
}
|
|
7094
|
+
}
|
|
7095
|
+
return false;
|
|
7096
|
+
} catch {
|
|
7097
|
+
return false;
|
|
7098
|
+
}
|
|
7099
|
+
}
|
|
7010
7100
|
var LaravelIntegration = class extends BaseBackendIntegration {
|
|
7011
7101
|
id = "laravel";
|
|
7012
7102
|
name = "Laravel";
|
|
@@ -7018,6 +7108,8 @@ var LaravelIntegration = class extends BaseBackendIntegration {
|
|
|
7018
7108
|
this.config = {
|
|
7019
7109
|
projectRoot,
|
|
7020
7110
|
csrfProtection: true,
|
|
7111
|
+
isLivewire: detectLivewire(projectRoot),
|
|
7112
|
+
isAlpine: detectAlpine(projectRoot),
|
|
7021
7113
|
...config
|
|
7022
7114
|
};
|
|
7023
7115
|
}
|
|
@@ -7037,12 +7129,12 @@ var LaravelIntegration = class extends BaseBackendIntegration {
|
|
|
7037
7129
|
indicators: ["composer.json: laravel/lumen-framework"]
|
|
7038
7130
|
};
|
|
7039
7131
|
}
|
|
7040
|
-
const hasComposerJson = (0,
|
|
7041
|
-
const hasArtisan = (0,
|
|
7042
|
-
const hasConfig = (0,
|
|
7043
|
-
const hasApp = (0,
|
|
7044
|
-
const hasRoutes = (0,
|
|
7045
|
-
const hasEnvExample = (0,
|
|
7132
|
+
const hasComposerJson = (0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "composer.json"));
|
|
7133
|
+
const hasArtisan = (0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "artisan"));
|
|
7134
|
+
const hasConfig = (0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "config"));
|
|
7135
|
+
const hasApp = (0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "app"));
|
|
7136
|
+
const hasRoutes = (0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "routes"));
|
|
7137
|
+
const hasEnvExample = (0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, ".env.example"));
|
|
7046
7138
|
if (!hasComposerJson || !hasLaravelDependency(projectRoot)) {
|
|
7047
7139
|
return {
|
|
7048
7140
|
detected: false,
|
|
@@ -7069,7 +7161,8 @@ var LaravelIntegration = class extends BaseBackendIntegration {
|
|
|
7069
7161
|
hasRoutes && "routes/",
|
|
7070
7162
|
hasEnvExample && ".env.example",
|
|
7071
7163
|
isSPA && "SPA mode (Vue/React/Inertia)",
|
|
7072
|
-
isLivewire && "Livewire"
|
|
7164
|
+
isLivewire && "Livewire",
|
|
7165
|
+
this.config.isAlpine && "Alpine.js"
|
|
7073
7166
|
].filter(Boolean);
|
|
7074
7167
|
const indicatorCount = indicators.length;
|
|
7075
7168
|
const confidence = indicatorCount >= 4 ? "high" : indicatorCount >= 2 ? "medium" : "low";
|
|
@@ -7088,6 +7181,7 @@ var LaravelIntegration = class extends BaseBackendIntegration {
|
|
|
7088
7181
|
generateServiceWorkerConfig() {
|
|
7089
7182
|
const isSPA = this.config.isSPA ?? false;
|
|
7090
7183
|
const isLivewire = this.config.isLivewire ?? false;
|
|
7184
|
+
const isAlpine = this.config.isAlpine ?? false;
|
|
7091
7185
|
const staticRoutes = [
|
|
7092
7186
|
{
|
|
7093
7187
|
pattern: "*.{js,css,woff,woff2,ttf,otf}",
|
|
@@ -7136,27 +7230,78 @@ var LaravelIntegration = class extends BaseBackendIntegration {
|
|
|
7136
7230
|
description: "Image files"
|
|
7137
7231
|
}
|
|
7138
7232
|
];
|
|
7139
|
-
const customRoutes =
|
|
7140
|
-
|
|
7233
|
+
const customRoutes = [];
|
|
7234
|
+
if (isLivewire) {
|
|
7235
|
+
customRoutes.push({
|
|
7141
7236
|
pattern: "/livewire/**",
|
|
7142
7237
|
strategy: {
|
|
7143
|
-
|
|
7144
|
-
|
|
7238
|
+
name: "NetworkFirst",
|
|
7239
|
+
cacheName: "laravel-livewire-cache",
|
|
7240
|
+
networkTimeoutSeconds: 5,
|
|
7241
|
+
expiration: {
|
|
7242
|
+
maxEntries: 50,
|
|
7243
|
+
maxAgeSeconds: 600
|
|
7244
|
+
// 10 minutes
|
|
7245
|
+
}
|
|
7145
7246
|
},
|
|
7146
7247
|
priority: 15,
|
|
7147
7248
|
description: "Livewire AJAX endpoints"
|
|
7148
|
-
}
|
|
7149
|
-
|
|
7249
|
+
});
|
|
7250
|
+
}
|
|
7251
|
+
if (isAlpine) {
|
|
7252
|
+
customRoutes.push({
|
|
7253
|
+
pattern: "/alpine/**",
|
|
7254
|
+
strategy: {
|
|
7255
|
+
name: "NetworkFirst",
|
|
7256
|
+
cacheName: "laravel-alpine-cache",
|
|
7257
|
+
networkTimeoutSeconds: 3,
|
|
7258
|
+
expiration: {
|
|
7259
|
+
maxEntries: 30,
|
|
7260
|
+
maxAgeSeconds: 300
|
|
7261
|
+
// 5 minutes
|
|
7262
|
+
}
|
|
7263
|
+
},
|
|
7264
|
+
priority: 14,
|
|
7265
|
+
description: "Alpine.js component requests"
|
|
7266
|
+
});
|
|
7267
|
+
customRoutes.push({
|
|
7268
|
+
pattern: "/api/alpine/**",
|
|
7269
|
+
strategy: {
|
|
7270
|
+
name: "NetworkFirst",
|
|
7271
|
+
cacheName: "laravel-alpine-api-cache",
|
|
7272
|
+
networkTimeoutSeconds: 2,
|
|
7273
|
+
expiration: {
|
|
7274
|
+
maxEntries: 40,
|
|
7275
|
+
maxAgeSeconds: 300
|
|
7276
|
+
// 5 minutes
|
|
7277
|
+
}
|
|
7278
|
+
},
|
|
7279
|
+
priority: 14,
|
|
7280
|
+
description: "Alpine.js API endpoints"
|
|
7281
|
+
});
|
|
7282
|
+
}
|
|
7283
|
+
if (isLivewire && isAlpine) {
|
|
7284
|
+
customRoutes.push({
|
|
7285
|
+
pattern: "/*.x-**",
|
|
7286
|
+
strategy: {
|
|
7287
|
+
name: "NetworkFirst",
|
|
7288
|
+
cacheName: "laravel-interactive-cache",
|
|
7289
|
+
networkTimeoutSeconds: 4,
|
|
7290
|
+
expiration: {
|
|
7291
|
+
maxEntries: 50,
|
|
7292
|
+
maxAgeSeconds: 600
|
|
7293
|
+
}
|
|
7294
|
+
},
|
|
7295
|
+
priority: 16,
|
|
7296
|
+
description: "Interactive component requests (Livewire + Alpine)"
|
|
7297
|
+
});
|
|
7298
|
+
}
|
|
7150
7299
|
return {
|
|
7151
7300
|
destination: "public/sw.js",
|
|
7152
7301
|
staticRoutes,
|
|
7153
7302
|
apiRoutes,
|
|
7154
7303
|
imageRoutes,
|
|
7155
|
-
customRoutes,
|
|
7156
|
-
offline: {
|
|
7157
|
-
fallbackPage: "/offline",
|
|
7158
|
-
fallbackImage: isSPA ? "/images/placeholder.png" : void 0
|
|
7159
|
-
},
|
|
7304
|
+
customRoutes: customRoutes.length > 0 ? customRoutes : void 0,
|
|
7160
7305
|
features: isSPA ? { hydration: true } : void 0
|
|
7161
7306
|
};
|
|
7162
7307
|
}
|
|
@@ -7254,7 +7399,23 @@ class PWAMiddleware
|
|
|
7254
7399
|
* Get API pattern routes for intelligent caching
|
|
7255
7400
|
*/
|
|
7256
7401
|
getApiPatterns() {
|
|
7257
|
-
|
|
7402
|
+
const patterns = ["/api/**", "/graphql"];
|
|
7403
|
+
if (this.config.isLivewire) {
|
|
7404
|
+
patterns.push("/livewire/**");
|
|
7405
|
+
}
|
|
7406
|
+
if (this.config.isAlpine) {
|
|
7407
|
+
patterns.push("/alpine/**", "/api/alpine/**");
|
|
7408
|
+
}
|
|
7409
|
+
return patterns;
|
|
7410
|
+
}
|
|
7411
|
+
/**
|
|
7412
|
+
* Get interactive framework configuration (Livewire + Alpine)
|
|
7413
|
+
*/
|
|
7414
|
+
getInteractiveFrameworks() {
|
|
7415
|
+
return {
|
|
7416
|
+
livewire: this.config.isLivewire ?? false,
|
|
7417
|
+
alpine: this.config.isAlpine ?? false
|
|
7418
|
+
};
|
|
7258
7419
|
}
|
|
7259
7420
|
/**
|
|
7260
7421
|
* Get static asset patterns
|
|
@@ -7275,27 +7436,27 @@ class PWAMiddleware
|
|
|
7275
7436
|
const warnings = [];
|
|
7276
7437
|
const suggestions = [];
|
|
7277
7438
|
const projectRoot = this.config.projectRoot;
|
|
7278
|
-
if (!(0,
|
|
7439
|
+
if (!(0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "public/manifest.json"))) {
|
|
7279
7440
|
warnings.push("manifest.json not found in public directory");
|
|
7280
7441
|
suggestions.push("Run: `npm run build` to generate manifest.json");
|
|
7281
7442
|
}
|
|
7282
|
-
if (!(0,
|
|
7443
|
+
if (!(0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "public/sw.js"))) {
|
|
7283
7444
|
warnings.push("Service worker (sw.js) not found in public directory");
|
|
7284
7445
|
suggestions.push("Run PWA generation tool to create service worker");
|
|
7285
7446
|
}
|
|
7286
|
-
const hasUrlRewrite = (0,
|
|
7447
|
+
const hasUrlRewrite = (0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "public/.htaccess")) || (0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "public/web.config"));
|
|
7287
7448
|
if (!hasUrlRewrite) {
|
|
7288
7449
|
warnings.push("URL rewrite configuration not found");
|
|
7289
7450
|
suggestions.push(
|
|
7290
7451
|
"Ensure your web server properly routes requests to index.php"
|
|
7291
7452
|
);
|
|
7292
7453
|
}
|
|
7293
|
-
if (!(0,
|
|
7454
|
+
if (!(0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "app/Http/Middleware/PWAMiddleware.php"))) {
|
|
7294
7455
|
suggestions.push(
|
|
7295
7456
|
"Consider registering PWAMiddleware for optimal PWA support"
|
|
7296
7457
|
);
|
|
7297
7458
|
}
|
|
7298
|
-
if (!(0,
|
|
7459
|
+
if (!(0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "resources/views/offline.blade.php"))) {
|
|
7299
7460
|
warnings.push("Offline fallback view not found");
|
|
7300
7461
|
suggestions.push(
|
|
7301
7462
|
"Create resources/views/offline.blade.php for offline experience"
|
|
@@ -7313,11 +7474,11 @@ class PWAMiddleware
|
|
|
7313
7474
|
return process.env.APP_NAME;
|
|
7314
7475
|
}
|
|
7315
7476
|
try {
|
|
7316
|
-
const composerPath = (0,
|
|
7317
|
-
if (!(0,
|
|
7477
|
+
const composerPath = (0, import_node_path8.join)(this.config.projectRoot, "composer.json");
|
|
7478
|
+
if (!(0, import_node_fs8.existsSync)(composerPath)) {
|
|
7318
7479
|
return "Laravel App";
|
|
7319
7480
|
}
|
|
7320
|
-
const content = (0,
|
|
7481
|
+
const content = (0, import_node_fs8.readFileSync)(composerPath, "utf-8");
|
|
7321
7482
|
const composer = JSON.parse(content);
|
|
7322
7483
|
const name = typeof composer.name === "string" ? composer.name : "";
|
|
7323
7484
|
if (!name) {
|
|
@@ -7332,15 +7493,15 @@ class PWAMiddleware
|
|
|
7332
7493
|
};
|
|
7333
7494
|
|
|
7334
7495
|
// src/backends/symfony.ts
|
|
7335
|
-
var
|
|
7336
|
-
var
|
|
7496
|
+
var import_node_fs9 = require("fs");
|
|
7497
|
+
var import_node_path9 = require("path");
|
|
7337
7498
|
function detectSymfonyVersion(projectRoot) {
|
|
7338
7499
|
try {
|
|
7339
|
-
const composerPath = (0,
|
|
7340
|
-
if (!(0,
|
|
7500
|
+
const composerPath = (0, import_node_path9.join)(projectRoot, "composer.json");
|
|
7501
|
+
if (!(0, import_node_fs9.existsSync)(composerPath)) {
|
|
7341
7502
|
return null;
|
|
7342
7503
|
}
|
|
7343
|
-
const content = (0,
|
|
7504
|
+
const content = (0, import_node_fs9.readFileSync)(composerPath, "utf-8");
|
|
7344
7505
|
const composer = JSON.parse(content);
|
|
7345
7506
|
const requires = composer.require;
|
|
7346
7507
|
const requiresDev = composer["require-dev"];
|
|
@@ -7361,11 +7522,11 @@ function detectSymfonyVersion(projectRoot) {
|
|
|
7361
7522
|
}
|
|
7362
7523
|
function hasSymfonyDependency(projectRoot) {
|
|
7363
7524
|
try {
|
|
7364
|
-
const composerPath = (0,
|
|
7365
|
-
if (!(0,
|
|
7525
|
+
const composerPath = (0, import_node_path9.join)(projectRoot, "composer.json");
|
|
7526
|
+
if (!(0, import_node_fs9.existsSync)(composerPath)) {
|
|
7366
7527
|
return false;
|
|
7367
7528
|
}
|
|
7368
|
-
const content = (0,
|
|
7529
|
+
const content = (0, import_node_fs9.readFileSync)(composerPath, "utf-8");
|
|
7369
7530
|
const composer = JSON.parse(content);
|
|
7370
7531
|
const requires = composer.require;
|
|
7371
7532
|
const requiresDev = composer["require-dev"];
|
|
@@ -7375,36 +7536,21 @@ function hasSymfonyDependency(projectRoot) {
|
|
|
7375
7536
|
}
|
|
7376
7537
|
}
|
|
7377
7538
|
function detectSPAMode2(projectRoot) {
|
|
7378
|
-
|
|
7379
|
-
|
|
7380
|
-
return true;
|
|
7381
|
-
}
|
|
7382
|
-
const viteExists = (0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "vite.config.ts")) || (0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "vite.config.js")) || (0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "vite.config.mjs"));
|
|
7383
|
-
if (viteExists) {
|
|
7384
|
-
const vitePath = (0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "vite.config.ts")) ? (0, import_node_path8.join)(projectRoot, "vite.config.ts") : (0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "vite.config.js")) ? (0, import_node_path8.join)(projectRoot, "vite.config.js") : (0, import_node_path8.join)(projectRoot, "vite.config.mjs");
|
|
7385
|
-
const viteContent = (0, import_node_fs8.readFileSync)(vitePath, "utf-8");
|
|
7386
|
-
return viteContent.includes("vue") || viteContent.includes("react") || viteContent.includes("svelte");
|
|
7387
|
-
}
|
|
7388
|
-
const packagePath = (0, import_node_path8.join)(projectRoot, "package.json");
|
|
7389
|
-
if ((0, import_node_fs8.existsSync)(packagePath)) {
|
|
7390
|
-
const content = (0, import_node_fs8.readFileSync)(packagePath, "utf-8");
|
|
7391
|
-
const pkg = JSON.parse(content);
|
|
7392
|
-
const dependencies = pkg.dependencies;
|
|
7393
|
-
const devDependencies = pkg.devDependencies;
|
|
7394
|
-
return !!(dependencies?.vue || dependencies?.react || dependencies?.svelte || devDependencies?.vue || devDependencies?.react || devDependencies?.svelte);
|
|
7395
|
-
}
|
|
7396
|
-
return false;
|
|
7397
|
-
} catch {
|
|
7398
|
-
return false;
|
|
7539
|
+
if ((0, import_node_fs9.existsSync)((0, import_node_path9.join)(projectRoot, "webpack.config.js")) || (0, import_node_fs9.existsSync)((0, import_node_path9.join)(projectRoot, "webpack.config.ts"))) {
|
|
7540
|
+
return true;
|
|
7399
7541
|
}
|
|
7542
|
+
return detectSPAFromViteAndPackage(projectRoot, {
|
|
7543
|
+
viteIncludes: ["vue", "react", "svelte"],
|
|
7544
|
+
packageKeys: ["vue", "react", "svelte"]
|
|
7545
|
+
});
|
|
7400
7546
|
}
|
|
7401
7547
|
function detectAPIPlatform(projectRoot) {
|
|
7402
7548
|
try {
|
|
7403
|
-
const composerPath = (0,
|
|
7404
|
-
if (!(0,
|
|
7549
|
+
const composerPath = (0, import_node_path9.join)(projectRoot, "composer.json");
|
|
7550
|
+
if (!(0, import_node_fs9.existsSync)(composerPath)) {
|
|
7405
7551
|
return false;
|
|
7406
7552
|
}
|
|
7407
|
-
const content = (0,
|
|
7553
|
+
const content = (0, import_node_fs9.readFileSync)(composerPath, "utf-8");
|
|
7408
7554
|
const composer = JSON.parse(content);
|
|
7409
7555
|
const requires = composer.require;
|
|
7410
7556
|
const requiresDev = composer["require-dev"];
|
|
@@ -7415,11 +7561,11 @@ function detectAPIPlatform(projectRoot) {
|
|
|
7415
7561
|
}
|
|
7416
7562
|
function parseEnv(projectRoot) {
|
|
7417
7563
|
try {
|
|
7418
|
-
const envPath = (0,
|
|
7419
|
-
if (!(0,
|
|
7564
|
+
const envPath = (0, import_node_path9.join)(projectRoot, ".env");
|
|
7565
|
+
if (!(0, import_node_fs9.existsSync)(envPath)) {
|
|
7420
7566
|
return {};
|
|
7421
7567
|
}
|
|
7422
|
-
const content = (0,
|
|
7568
|
+
const content = (0, import_node_fs9.readFileSync)(envPath, "utf-8");
|
|
7423
7569
|
const env = {};
|
|
7424
7570
|
content.split("\n").forEach((line) => {
|
|
7425
7571
|
const trimmed = line.trim();
|
|
@@ -7470,9 +7616,9 @@ var SymfonyIntegration = class extends BaseBackendIntegration {
|
|
|
7470
7616
|
}
|
|
7471
7617
|
indicators.push("composer.json: symfony/framework-bundle");
|
|
7472
7618
|
const version = detectSymfonyVersion(projectRoot);
|
|
7473
|
-
const hasPublicIndex = (0,
|
|
7474
|
-
const hasServicesConfig = (0,
|
|
7475
|
-
const hasConfigDir = (0,
|
|
7619
|
+
const hasPublicIndex = (0, import_node_fs9.existsSync)((0, import_node_path9.join)(projectRoot, "public/index.php"));
|
|
7620
|
+
const hasServicesConfig = (0, import_node_fs9.existsSync)((0, import_node_path9.join)(projectRoot, "config/services.yaml")) || (0, import_node_fs9.existsSync)((0, import_node_path9.join)(projectRoot, "config/services.yml"));
|
|
7621
|
+
const hasConfigDir = (0, import_node_fs9.existsSync)((0, import_node_path9.join)(projectRoot, "config"));
|
|
7476
7622
|
if (hasPublicIndex) indicators.push("public/index.php");
|
|
7477
7623
|
if (hasServicesConfig) indicators.push("config/services.yaml");
|
|
7478
7624
|
if (hasConfigDir) indicators.push("config/");
|
|
@@ -7597,15 +7743,15 @@ var SymfonyIntegration = class extends BaseBackendIntegration {
|
|
|
7597
7743
|
const warnings = [];
|
|
7598
7744
|
const suggestions = [];
|
|
7599
7745
|
const projectRoot = this.config.projectRoot;
|
|
7600
|
-
if (!(0,
|
|
7746
|
+
if (!(0, import_node_fs9.existsSync)((0, import_node_path9.join)(projectRoot, "public/manifest.json"))) {
|
|
7601
7747
|
warnings.push("manifest.json not found in public directory");
|
|
7602
7748
|
suggestions.push("Generate manifest.json in public directory");
|
|
7603
7749
|
}
|
|
7604
|
-
if (!(0,
|
|
7750
|
+
if (!(0, import_node_fs9.existsSync)((0, import_node_path9.join)(projectRoot, "public/service-worker.js"))) {
|
|
7605
7751
|
warnings.push("Service worker not found in public directory");
|
|
7606
7752
|
suggestions.push("Generate service worker in public directory");
|
|
7607
7753
|
}
|
|
7608
|
-
if (!(0,
|
|
7754
|
+
if (!(0, import_node_fs9.existsSync)((0, import_node_path9.join)(projectRoot, "templates/offline.html.twig"))) {
|
|
7609
7755
|
warnings.push("Offline fallback template not found");
|
|
7610
7756
|
suggestions.push(
|
|
7611
7757
|
"Create templates/offline.html.twig for offline experience"
|
|
@@ -7623,11 +7769,11 @@ var SymfonyIntegration = class extends BaseBackendIntegration {
|
|
|
7623
7769
|
return env.APP_NAME;
|
|
7624
7770
|
}
|
|
7625
7771
|
try {
|
|
7626
|
-
const composerPath = (0,
|
|
7627
|
-
if (!(0,
|
|
7772
|
+
const composerPath = (0, import_node_path9.join)(this.config.projectRoot, "composer.json");
|
|
7773
|
+
if (!(0, import_node_fs9.existsSync)(composerPath)) {
|
|
7628
7774
|
return "Symfony App";
|
|
7629
7775
|
}
|
|
7630
|
-
const content = (0,
|
|
7776
|
+
const content = (0, import_node_fs9.readFileSync)(composerPath, "utf-8");
|
|
7631
7777
|
const composer = JSON.parse(content);
|
|
7632
7778
|
const name = typeof composer.name === "string" ? composer.name : "";
|
|
7633
7779
|
if (!name) {
|
|
@@ -7643,16 +7789,28 @@ var SymfonyIntegration = class extends BaseBackendIntegration {
|
|
|
7643
7789
|
};
|
|
7644
7790
|
|
|
7645
7791
|
// src/backends/django.ts
|
|
7646
|
-
var
|
|
7647
|
-
var
|
|
7648
|
-
|
|
7792
|
+
var import_node_fs11 = require("fs");
|
|
7793
|
+
var import_node_path11 = require("path");
|
|
7794
|
+
|
|
7795
|
+
// src/backends/python-deps.ts
|
|
7796
|
+
var import_node_fs10 = require("fs");
|
|
7797
|
+
var import_node_path10 = require("path");
|
|
7798
|
+
function getPythonPackageVersion(projectRoot, packageName) {
|
|
7799
|
+
const reqName = packageName.replace(/-/g, "[-_]");
|
|
7800
|
+
const reqRegex = new RegExp(
|
|
7801
|
+
`${reqName}[>=<~!]*(\\d+)(?:\\.(\\d+))?(?:\\.(\\d+))?`,
|
|
7802
|
+
"i"
|
|
7803
|
+
);
|
|
7804
|
+
const pyprojectName = packageName.toLowerCase().replace(/-/g, "_");
|
|
7805
|
+
const pyprojectRegex = new RegExp(
|
|
7806
|
+
`${pyprojectName}\\s*=\\s*["']?[>=<~!]*(\\d+)(?:\\.(\\d+))?(?:\\.(\\d+))?`,
|
|
7807
|
+
"i"
|
|
7808
|
+
);
|
|
7649
7809
|
try {
|
|
7650
|
-
const requirementsPath = (0,
|
|
7651
|
-
if ((0,
|
|
7652
|
-
const content = (0,
|
|
7653
|
-
const match = content.match(
|
|
7654
|
-
/Django[>=<~!]*(\d+)(?:\.(\d+))?(?:\.(\d+))?/i
|
|
7655
|
-
);
|
|
7810
|
+
const requirementsPath = (0, import_node_path10.join)(projectRoot, "requirements.txt");
|
|
7811
|
+
if ((0, import_node_fs10.existsSync)(requirementsPath)) {
|
|
7812
|
+
const content = (0, import_node_fs10.readFileSync)(requirementsPath, "utf-8");
|
|
7813
|
+
const match = content.match(reqRegex);
|
|
7656
7814
|
if (match) {
|
|
7657
7815
|
const major = match[1] ?? "0";
|
|
7658
7816
|
const minor = match[2] ?? "0";
|
|
@@ -7663,12 +7821,10 @@ function detectDjangoVersion(projectRoot) {
|
|
|
7663
7821
|
} catch {
|
|
7664
7822
|
}
|
|
7665
7823
|
try {
|
|
7666
|
-
const pyprojectPath = (0,
|
|
7667
|
-
if ((0,
|
|
7668
|
-
const content = (0,
|
|
7669
|
-
const match = content.match(
|
|
7670
|
-
/django\s*=\s*["']?[>=<~!]*(\d+)(?:\.(\d+))?(?:\.(\d+))?/i
|
|
7671
|
-
);
|
|
7824
|
+
const pyprojectPath = (0, import_node_path10.join)(projectRoot, "pyproject.toml");
|
|
7825
|
+
if ((0, import_node_fs10.existsSync)(pyprojectPath)) {
|
|
7826
|
+
const content = (0, import_node_fs10.readFileSync)(pyprojectPath, "utf-8");
|
|
7827
|
+
const match = content.match(pyprojectRegex);
|
|
7672
7828
|
if (match) {
|
|
7673
7829
|
const major = match[1] ?? "0";
|
|
7674
7830
|
const minor = match[2] ?? "0";
|
|
@@ -7680,45 +7836,48 @@ function detectDjangoVersion(projectRoot) {
|
|
|
7680
7836
|
}
|
|
7681
7837
|
return null;
|
|
7682
7838
|
}
|
|
7683
|
-
function
|
|
7839
|
+
function hasPythonPackage(projectRoot, packageName) {
|
|
7684
7840
|
try {
|
|
7685
|
-
const requirementsPath = (0,
|
|
7686
|
-
if ((0,
|
|
7687
|
-
const content = (0,
|
|
7688
|
-
if (
|
|
7841
|
+
const requirementsPath = (0, import_node_path10.join)(projectRoot, "requirements.txt");
|
|
7842
|
+
if ((0, import_node_fs10.existsSync)(requirementsPath)) {
|
|
7843
|
+
const content = (0, import_node_fs10.readFileSync)(requirementsPath, "utf-8");
|
|
7844
|
+
if (new RegExp(packageName.replace(/-/g, "[-_]"), "i").test(content)) {
|
|
7689
7845
|
return true;
|
|
7690
7846
|
}
|
|
7691
7847
|
}
|
|
7692
|
-
const pyprojectPath = (0,
|
|
7693
|
-
if ((0,
|
|
7694
|
-
const content = (0,
|
|
7695
|
-
if (
|
|
7848
|
+
const pyprojectPath = (0, import_node_path10.join)(projectRoot, "pyproject.toml");
|
|
7849
|
+
if ((0, import_node_fs10.existsSync)(pyprojectPath)) {
|
|
7850
|
+
const content = (0, import_node_fs10.readFileSync)(pyprojectPath, "utf-8");
|
|
7851
|
+
if (new RegExp(packageName.toLowerCase().replace(/-/g, "_"), "i").test(
|
|
7852
|
+
content
|
|
7853
|
+
)) {
|
|
7696
7854
|
return true;
|
|
7697
7855
|
}
|
|
7698
7856
|
}
|
|
7699
|
-
return false;
|
|
7700
7857
|
} catch {
|
|
7701
|
-
return false;
|
|
7702
7858
|
}
|
|
7859
|
+
return false;
|
|
7703
7860
|
}
|
|
7861
|
+
|
|
7862
|
+
// src/backends/django.ts
|
|
7704
7863
|
function detectASGI(projectRoot) {
|
|
7705
7864
|
try {
|
|
7706
|
-
if ((0,
|
|
7865
|
+
if ((0, import_node_fs11.existsSync)((0, import_node_path11.join)(projectRoot, "asgi.py"))) {
|
|
7707
7866
|
return true;
|
|
7708
7867
|
}
|
|
7709
|
-
const settingsPath = (0,
|
|
7710
|
-
if ((0,
|
|
7711
|
-
const content = (0,
|
|
7868
|
+
const settingsPath = (0, import_node_path11.join)(projectRoot, "settings.py");
|
|
7869
|
+
if ((0, import_node_fs11.existsSync)(settingsPath)) {
|
|
7870
|
+
const content = (0, import_node_fs11.readFileSync)(settingsPath, "utf-8");
|
|
7712
7871
|
if (/ASGI_APPLICATION/i.test(content)) {
|
|
7713
7872
|
return true;
|
|
7714
7873
|
}
|
|
7715
7874
|
}
|
|
7716
|
-
const settingsDir = (0,
|
|
7717
|
-
if ((0,
|
|
7718
|
-
const files = (0,
|
|
7875
|
+
const settingsDir = (0, import_node_path11.join)(projectRoot, "settings");
|
|
7876
|
+
if ((0, import_node_fs11.existsSync)(settingsDir)) {
|
|
7877
|
+
const files = (0, import_node_fs11.readdirSync)(settingsDir);
|
|
7719
7878
|
for (const file of files) {
|
|
7720
7879
|
if (file.endsWith(".py")) {
|
|
7721
|
-
const content = (0,
|
|
7880
|
+
const content = (0, import_node_fs11.readFileSync)((0, import_node_path11.join)(settingsDir, file), "utf-8");
|
|
7722
7881
|
if (/ASGI_APPLICATION/i.test(content)) {
|
|
7723
7882
|
return true;
|
|
7724
7883
|
}
|
|
@@ -7731,17 +7890,13 @@ function detectASGI(projectRoot) {
|
|
|
7731
7890
|
}
|
|
7732
7891
|
}
|
|
7733
7892
|
function detectDjangoRESTFramework(projectRoot) {
|
|
7893
|
+
if (hasPythonPackage(projectRoot, "djangorestframework")) {
|
|
7894
|
+
return true;
|
|
7895
|
+
}
|
|
7734
7896
|
try {
|
|
7735
|
-
const
|
|
7736
|
-
if ((0,
|
|
7737
|
-
const content = (0,
|
|
7738
|
-
if (/djangorestframework|django-rest-framework/i.test(content)) {
|
|
7739
|
-
return true;
|
|
7740
|
-
}
|
|
7741
|
-
}
|
|
7742
|
-
const settingsPath = (0, import_node_path9.join)(projectRoot, "settings.py");
|
|
7743
|
-
if ((0, import_node_fs9.existsSync)(settingsPath)) {
|
|
7744
|
-
const content = (0, import_node_fs9.readFileSync)(settingsPath, "utf-8");
|
|
7897
|
+
const settingsPath = (0, import_node_path11.join)(projectRoot, "settings.py");
|
|
7898
|
+
if ((0, import_node_fs11.existsSync)(settingsPath)) {
|
|
7899
|
+
const content = (0, import_node_fs11.readFileSync)(settingsPath, "utf-8");
|
|
7745
7900
|
if (/rest_framework/i.test(content)) {
|
|
7746
7901
|
return true;
|
|
7747
7902
|
}
|
|
@@ -7754,9 +7909,9 @@ function detectDjangoRESTFramework(projectRoot) {
|
|
|
7754
7909
|
function extractStaticFilesConfig(projectRoot) {
|
|
7755
7910
|
const result = {};
|
|
7756
7911
|
try {
|
|
7757
|
-
const settingsPath = (0,
|
|
7758
|
-
if ((0,
|
|
7759
|
-
const content = (0,
|
|
7912
|
+
const settingsPath = (0, import_node_path11.join)(projectRoot, "settings.py");
|
|
7913
|
+
if ((0, import_node_fs11.existsSync)(settingsPath)) {
|
|
7914
|
+
const content = (0, import_node_fs11.readFileSync)(settingsPath, "utf-8");
|
|
7760
7915
|
const staticUrlMatch = content.match(/STATIC_URL\s*=\s*['"]([^'"]+)['"]/);
|
|
7761
7916
|
if (staticUrlMatch) {
|
|
7762
7917
|
result.staticUrl = staticUrlMatch[1];
|
|
@@ -7768,12 +7923,12 @@ function extractStaticFilesConfig(projectRoot) {
|
|
|
7768
7923
|
result.staticRoot = staticRootMatch[1];
|
|
7769
7924
|
}
|
|
7770
7925
|
}
|
|
7771
|
-
const settingsDir = (0,
|
|
7772
|
-
if ((0,
|
|
7773
|
-
const files = (0,
|
|
7926
|
+
const settingsDir = (0, import_node_path11.join)(projectRoot, "settings");
|
|
7927
|
+
if ((0, import_node_fs11.existsSync)(settingsDir)) {
|
|
7928
|
+
const files = (0, import_node_fs11.readdirSync)(settingsDir);
|
|
7774
7929
|
for (const file of files) {
|
|
7775
7930
|
if (file.endsWith(".py")) {
|
|
7776
|
-
const content = (0,
|
|
7931
|
+
const content = (0, import_node_fs11.readFileSync)((0, import_node_path11.join)(settingsDir, file), "utf-8");
|
|
7777
7932
|
if (!result.staticUrl) {
|
|
7778
7933
|
const staticUrlMatch = content.match(
|
|
7779
7934
|
/STATIC_URL\s*=\s*['"]([^'"]+)['"]/
|
|
@@ -7824,23 +7979,23 @@ var DjangoIntegration = class extends BaseBackendIntegration {
|
|
|
7824
7979
|
const projectRoot = this.config.projectRoot;
|
|
7825
7980
|
const indicators = [];
|
|
7826
7981
|
let confidence = "low";
|
|
7827
|
-
if ((0,
|
|
7982
|
+
if ((0, import_node_fs11.existsSync)((0, import_node_path11.join)(projectRoot, "manage.py"))) {
|
|
7828
7983
|
indicators.push("manage.py");
|
|
7829
7984
|
confidence = "medium";
|
|
7830
7985
|
}
|
|
7831
|
-
if ((0,
|
|
7986
|
+
if ((0, import_node_fs11.existsSync)((0, import_node_path11.join)(projectRoot, "settings.py")) || (0, import_node_fs11.existsSync)((0, import_node_path11.join)(projectRoot, "settings"))) {
|
|
7832
7987
|
indicators.push("settings.py or settings/");
|
|
7833
7988
|
if (confidence === "medium") {
|
|
7834
7989
|
confidence = "high";
|
|
7835
7990
|
}
|
|
7836
7991
|
}
|
|
7837
|
-
if ((0,
|
|
7992
|
+
if ((0, import_node_fs11.existsSync)((0, import_node_path11.join)(projectRoot, "urls.py"))) {
|
|
7838
7993
|
indicators.push("urls.py");
|
|
7839
7994
|
if (confidence === "medium") {
|
|
7840
7995
|
confidence = "high";
|
|
7841
7996
|
}
|
|
7842
7997
|
}
|
|
7843
|
-
if (
|
|
7998
|
+
if (hasPythonPackage(projectRoot, "Django")) {
|
|
7844
7999
|
indicators.push(
|
|
7845
8000
|
"Django dependency in requirements.txt or pyproject.toml"
|
|
7846
8001
|
);
|
|
@@ -7848,7 +8003,7 @@ var DjangoIntegration = class extends BaseBackendIntegration {
|
|
|
7848
8003
|
confidence = "high";
|
|
7849
8004
|
}
|
|
7850
8005
|
}
|
|
7851
|
-
const version =
|
|
8006
|
+
const version = getPythonPackageVersion(projectRoot, "Django");
|
|
7852
8007
|
return {
|
|
7853
8008
|
detected: indicators.length > 0,
|
|
7854
8009
|
framework: "django",
|
|
@@ -7996,12 +8151,12 @@ var DjangoIntegration = class extends BaseBackendIntegration {
|
|
|
7996
8151
|
const warnings = [];
|
|
7997
8152
|
const suggestions = [];
|
|
7998
8153
|
const projectRoot = this.config.projectRoot;
|
|
7999
|
-
if (!(0,
|
|
8154
|
+
if (!(0, import_node_fs11.existsSync)((0, import_node_path11.join)(projectRoot, "settings.py")) && !(0, import_node_fs11.existsSync)((0, import_node_path11.join)(projectRoot, "settings"))) {
|
|
8000
8155
|
errors.push("Django settings.py or settings/ directory not found");
|
|
8001
8156
|
}
|
|
8002
|
-
if ((0,
|
|
8157
|
+
if ((0, import_node_fs11.existsSync)((0, import_node_path11.join)(projectRoot, "settings.py"))) {
|
|
8003
8158
|
try {
|
|
8004
|
-
const content = (0,
|
|
8159
|
+
const content = (0, import_node_fs11.readFileSync)((0, import_node_path11.join)(projectRoot, "settings.py"), "utf-8");
|
|
8005
8160
|
if (!/STATIC_URL/i.test(content)) {
|
|
8006
8161
|
warnings.push("STATIC_URL not found in settings.py");
|
|
8007
8162
|
}
|
|
@@ -8013,10 +8168,10 @@ var DjangoIntegration = class extends BaseBackendIntegration {
|
|
|
8013
8168
|
} catch {
|
|
8014
8169
|
}
|
|
8015
8170
|
}
|
|
8016
|
-
if (!(0,
|
|
8171
|
+
if (!(0, import_node_fs11.existsSync)((0, import_node_path11.join)(projectRoot, "urls.py"))) {
|
|
8017
8172
|
warnings.push("urls.py not found in project root");
|
|
8018
8173
|
}
|
|
8019
|
-
if (!(0,
|
|
8174
|
+
if (!(0, import_node_fs11.existsSync)((0, import_node_path11.join)(projectRoot, "manage.py"))) {
|
|
8020
8175
|
errors.push("manage.py not found (required for Django projects)");
|
|
8021
8176
|
}
|
|
8022
8177
|
if (!this.config.isASGI) {
|
|
@@ -8063,78 +8218,22 @@ PWA_SW_PATH = STATIC_ROOT / 'sw.js'`,
|
|
|
8063
8218
|
};
|
|
8064
8219
|
|
|
8065
8220
|
// src/backends/flask.ts
|
|
8066
|
-
var
|
|
8067
|
-
var
|
|
8068
|
-
function detectFlaskVersion(projectRoot) {
|
|
8069
|
-
try {
|
|
8070
|
-
const requirementsPath = (0, import_node_path10.join)(projectRoot, "requirements.txt");
|
|
8071
|
-
if ((0, import_node_fs10.existsSync)(requirementsPath)) {
|
|
8072
|
-
const content = (0, import_node_fs10.readFileSync)(requirementsPath, "utf-8");
|
|
8073
|
-
const match = content.match(
|
|
8074
|
-
/Flask[>=<~!]*(\d+)(?:\.(\d+))?(?:\.(\d+))?/i
|
|
8075
|
-
);
|
|
8076
|
-
if (match) {
|
|
8077
|
-
const major = match[1] ?? "0";
|
|
8078
|
-
const minor = match[2] ?? "0";
|
|
8079
|
-
const patch = match[3] ?? "0";
|
|
8080
|
-
return `${major}.${minor}.${patch}`;
|
|
8081
|
-
}
|
|
8082
|
-
}
|
|
8083
|
-
} catch {
|
|
8084
|
-
}
|
|
8085
|
-
try {
|
|
8086
|
-
const pyprojectPath = (0, import_node_path10.join)(projectRoot, "pyproject.toml");
|
|
8087
|
-
if ((0, import_node_fs10.existsSync)(pyprojectPath)) {
|
|
8088
|
-
const content = (0, import_node_fs10.readFileSync)(pyprojectPath, "utf-8");
|
|
8089
|
-
const match = content.match(
|
|
8090
|
-
/flask\s*=\s*["']?[>=<~!]*(\d+)(?:\.(\d+))?(?:\.(\d+))?/i
|
|
8091
|
-
);
|
|
8092
|
-
if (match) {
|
|
8093
|
-
const major = match[1] ?? "0";
|
|
8094
|
-
const minor = match[2] ?? "0";
|
|
8095
|
-
const patch = match[3] ?? "0";
|
|
8096
|
-
return `${major}.${minor}.${patch}`;
|
|
8097
|
-
}
|
|
8098
|
-
}
|
|
8099
|
-
} catch {
|
|
8100
|
-
}
|
|
8101
|
-
return null;
|
|
8102
|
-
}
|
|
8103
|
-
function hasFlaskDependency(projectRoot) {
|
|
8104
|
-
try {
|
|
8105
|
-
const requirementsPath = (0, import_node_path10.join)(projectRoot, "requirements.txt");
|
|
8106
|
-
if ((0, import_node_fs10.existsSync)(requirementsPath)) {
|
|
8107
|
-
const content = (0, import_node_fs10.readFileSync)(requirementsPath, "utf-8");
|
|
8108
|
-
if (/Flask/i.test(content)) {
|
|
8109
|
-
return true;
|
|
8110
|
-
}
|
|
8111
|
-
}
|
|
8112
|
-
const pyprojectPath = (0, import_node_path10.join)(projectRoot, "pyproject.toml");
|
|
8113
|
-
if ((0, import_node_fs10.existsSync)(pyprojectPath)) {
|
|
8114
|
-
const content = (0, import_node_fs10.readFileSync)(pyprojectPath, "utf-8");
|
|
8115
|
-
if (/flask/i.test(content)) {
|
|
8116
|
-
return true;
|
|
8117
|
-
}
|
|
8118
|
-
}
|
|
8119
|
-
return false;
|
|
8120
|
-
} catch {
|
|
8121
|
-
return false;
|
|
8122
|
-
}
|
|
8123
|
-
}
|
|
8221
|
+
var import_node_fs12 = require("fs");
|
|
8222
|
+
var import_node_path12 = require("path");
|
|
8124
8223
|
function detectFlaskWTF(projectRoot) {
|
|
8125
8224
|
try {
|
|
8126
|
-
const requirementsPath = (0,
|
|
8127
|
-
if ((0,
|
|
8128
|
-
const content = (0,
|
|
8225
|
+
const requirementsPath = (0, import_node_path12.join)(projectRoot, "requirements.txt");
|
|
8226
|
+
if ((0, import_node_fs12.existsSync)(requirementsPath)) {
|
|
8227
|
+
const content = (0, import_node_fs12.readFileSync)(requirementsPath, "utf-8");
|
|
8129
8228
|
if (/Flask-WTF|flask-wtf/i.test(content)) {
|
|
8130
8229
|
return true;
|
|
8131
8230
|
}
|
|
8132
8231
|
}
|
|
8133
8232
|
const appFiles = ["app.py", "application.py"];
|
|
8134
8233
|
for (const file of appFiles) {
|
|
8135
|
-
const appPath = (0,
|
|
8136
|
-
if ((0,
|
|
8137
|
-
const content = (0,
|
|
8234
|
+
const appPath = (0, import_node_path12.join)(projectRoot, file);
|
|
8235
|
+
if ((0, import_node_fs12.existsSync)(appPath)) {
|
|
8236
|
+
const content = (0, import_node_fs12.readFileSync)(appPath, "utf-8");
|
|
8138
8237
|
if (/from flask_wtf|import FlaskForm|CSRFProtect/i.test(content)) {
|
|
8139
8238
|
return true;
|
|
8140
8239
|
}
|
|
@@ -8147,18 +8246,18 @@ function detectFlaskWTF(projectRoot) {
|
|
|
8147
8246
|
}
|
|
8148
8247
|
function detectFlaskRESTful(projectRoot) {
|
|
8149
8248
|
try {
|
|
8150
|
-
const requirementsPath = (0,
|
|
8151
|
-
if ((0,
|
|
8152
|
-
const content = (0,
|
|
8249
|
+
const requirementsPath = (0, import_node_path12.join)(projectRoot, "requirements.txt");
|
|
8250
|
+
if ((0, import_node_fs12.existsSync)(requirementsPath)) {
|
|
8251
|
+
const content = (0, import_node_fs12.readFileSync)(requirementsPath, "utf-8");
|
|
8153
8252
|
if (/Flask-RESTful|flask-restful/i.test(content)) {
|
|
8154
8253
|
return true;
|
|
8155
8254
|
}
|
|
8156
8255
|
}
|
|
8157
8256
|
const appFiles = ["app.py", "application.py"];
|
|
8158
8257
|
for (const file of appFiles) {
|
|
8159
|
-
const appPath = (0,
|
|
8160
|
-
if ((0,
|
|
8161
|
-
const content = (0,
|
|
8258
|
+
const appPath = (0, import_node_path12.join)(projectRoot, file);
|
|
8259
|
+
if ((0, import_node_fs12.existsSync)(appPath)) {
|
|
8260
|
+
const content = (0, import_node_fs12.readFileSync)(appPath, "utf-8");
|
|
8162
8261
|
if (/from flask_restful|import Api|import Resource/i.test(content)) {
|
|
8163
8262
|
return true;
|
|
8164
8263
|
}
|
|
@@ -8169,14 +8268,71 @@ function detectFlaskRESTful(projectRoot) {
|
|
|
8169
8268
|
return false;
|
|
8170
8269
|
}
|
|
8171
8270
|
}
|
|
8271
|
+
function detectBlueprints(projectRoot) {
|
|
8272
|
+
const blueprints = [];
|
|
8273
|
+
try {
|
|
8274
|
+
const potentialBlueprintDirs = [
|
|
8275
|
+
"blueprints",
|
|
8276
|
+
"routes",
|
|
8277
|
+
"apps",
|
|
8278
|
+
"modules",
|
|
8279
|
+
"features"
|
|
8280
|
+
];
|
|
8281
|
+
for (const dir of potentialBlueprintDirs) {
|
|
8282
|
+
const blueprintPath = (0, import_node_path12.join)(projectRoot, dir);
|
|
8283
|
+
if ((0, import_node_fs12.existsSync)(blueprintPath)) {
|
|
8284
|
+
try {
|
|
8285
|
+
const entries = (0, import_node_fs12.readdirSync)(blueprintPath, {
|
|
8286
|
+
withFileTypes: true
|
|
8287
|
+
});
|
|
8288
|
+
for (const entry of entries) {
|
|
8289
|
+
if (entry.isDirectory() && !entry.name.startsWith("_") && !entry.name.startsWith(".")) {
|
|
8290
|
+
const blueprintFile = (0, import_node_path12.join)(
|
|
8291
|
+
blueprintPath,
|
|
8292
|
+
entry.name,
|
|
8293
|
+
"__init__.py"
|
|
8294
|
+
);
|
|
8295
|
+
if ((0, import_node_fs12.existsSync)(blueprintFile)) {
|
|
8296
|
+
const content = (0, import_node_fs12.readFileSync)(blueprintFile, "utf-8");
|
|
8297
|
+
const blueprintNameMatch = content.match(
|
|
8298
|
+
/Blueprint\s*\(\s*['"]([^'"]+)['"]/
|
|
8299
|
+
);
|
|
8300
|
+
const blueprintName = blueprintNameMatch?.[1] || entry.name;
|
|
8301
|
+
const urlPrefixMatch = content.match(
|
|
8302
|
+
/url_prefix\s*=\s*['"]([^'"]+)['"]/
|
|
8303
|
+
);
|
|
8304
|
+
const prefix = urlPrefixMatch?.[1];
|
|
8305
|
+
const routeMatches = content.match(
|
|
8306
|
+
/@bp\.route\s*\(\s*['"]([^'"]+)['"]/g
|
|
8307
|
+
);
|
|
8308
|
+
const routes = routeMatches?.map(
|
|
8309
|
+
(route) => route.match(/['"]([^'"]+)['"]/)?.[1] || ""
|
|
8310
|
+
) || [];
|
|
8311
|
+
blueprints.push({
|
|
8312
|
+
name: blueprintName,
|
|
8313
|
+
prefix,
|
|
8314
|
+
folder: (0, import_node_path12.join)(dir, entry.name),
|
|
8315
|
+
routes
|
|
8316
|
+
});
|
|
8317
|
+
}
|
|
8318
|
+
}
|
|
8319
|
+
}
|
|
8320
|
+
} catch {
|
|
8321
|
+
}
|
|
8322
|
+
}
|
|
8323
|
+
}
|
|
8324
|
+
} catch {
|
|
8325
|
+
}
|
|
8326
|
+
return blueprints;
|
|
8327
|
+
}
|
|
8172
8328
|
function extractStaticFilesConfig2(projectRoot) {
|
|
8173
8329
|
const result = {};
|
|
8174
8330
|
try {
|
|
8175
8331
|
const appFiles = ["app.py", "application.py"];
|
|
8176
8332
|
for (const file of appFiles) {
|
|
8177
|
-
const appPath = (0,
|
|
8178
|
-
if ((0,
|
|
8179
|
-
const content = (0,
|
|
8333
|
+
const appPath = (0, import_node_path12.join)(projectRoot, file);
|
|
8334
|
+
if ((0, import_node_fs12.existsSync)(appPath)) {
|
|
8335
|
+
const content = (0, import_node_fs12.readFileSync)(appPath, "utf-8");
|
|
8180
8336
|
const staticFolderMatch = content.match(
|
|
8181
8337
|
/static_folder\s*=\s*['"]([^'"]+)['"]/
|
|
8182
8338
|
);
|
|
@@ -8204,12 +8360,14 @@ var FlaskIntegration = class extends BaseBackendIntegration {
|
|
|
8204
8360
|
constructor(projectRoot, config) {
|
|
8205
8361
|
super();
|
|
8206
8362
|
const staticConfig = extractStaticFilesConfig2(projectRoot);
|
|
8363
|
+
const blueprints = detectBlueprints(projectRoot);
|
|
8207
8364
|
this.config = {
|
|
8208
8365
|
projectRoot,
|
|
8209
8366
|
hasFlaskWTF: detectFlaskWTF(projectRoot),
|
|
8210
8367
|
hasFlaskRESTful: detectFlaskRESTful(projectRoot),
|
|
8211
8368
|
staticUrl: staticConfig.staticUrl,
|
|
8212
8369
|
staticFolder: staticConfig.staticFolder,
|
|
8370
|
+
blueprints,
|
|
8213
8371
|
...config
|
|
8214
8372
|
};
|
|
8215
8373
|
}
|
|
@@ -8222,23 +8380,31 @@ var FlaskIntegration = class extends BaseBackendIntegration {
|
|
|
8222
8380
|
const projectRoot = this.config.projectRoot;
|
|
8223
8381
|
const indicators = [];
|
|
8224
8382
|
let confidence = "low";
|
|
8225
|
-
if ((0,
|
|
8383
|
+
if ((0, import_node_fs12.existsSync)((0, import_node_path12.join)(projectRoot, "app.py")) || (0, import_node_fs12.existsSync)((0, import_node_path12.join)(projectRoot, "application.py"))) {
|
|
8226
8384
|
indicators.push("app.py or application.py");
|
|
8227
8385
|
confidence = "medium";
|
|
8228
8386
|
}
|
|
8229
|
-
if (
|
|
8387
|
+
if (hasPythonPackage(projectRoot, "Flask")) {
|
|
8230
8388
|
indicators.push("Flask dependency in requirements.txt or pyproject.toml");
|
|
8231
8389
|
if (confidence === "medium") {
|
|
8232
8390
|
confidence = "high";
|
|
8233
8391
|
}
|
|
8234
8392
|
}
|
|
8235
|
-
if ((0,
|
|
8393
|
+
if ((0, import_node_fs12.existsSync)((0, import_node_path12.join)(projectRoot, "templates")) || (0, import_node_fs12.existsSync)((0, import_node_path12.join)(projectRoot, "static"))) {
|
|
8236
8394
|
indicators.push("Flask structure (templates/ or static/)");
|
|
8237
8395
|
if (confidence === "medium") {
|
|
8238
8396
|
confidence = "high";
|
|
8239
8397
|
}
|
|
8240
8398
|
}
|
|
8241
|
-
|
|
8399
|
+
if (this.config.blueprints && this.config.blueprints.length > 0) {
|
|
8400
|
+
indicators.push(
|
|
8401
|
+
`Flask blueprints (${this.config.blueprints.length} detected)`
|
|
8402
|
+
);
|
|
8403
|
+
if (confidence < "high") {
|
|
8404
|
+
confidence = "high";
|
|
8405
|
+
}
|
|
8406
|
+
}
|
|
8407
|
+
const version = getPythonPackageVersion(projectRoot, "Flask");
|
|
8242
8408
|
return {
|
|
8243
8409
|
detected: indicators.length > 0,
|
|
8244
8410
|
framework: "flask",
|
|
@@ -8285,6 +8451,26 @@ var FlaskIntegration = class extends BaseBackendIntegration {
|
|
|
8285
8451
|
description: "Flask API endpoints"
|
|
8286
8452
|
}
|
|
8287
8453
|
];
|
|
8454
|
+
if (this.config.blueprints && this.config.blueprints.length > 0) {
|
|
8455
|
+
for (const blueprint of this.config.blueprints) {
|
|
8456
|
+
const prefix = blueprint.prefix || `/${blueprint.name.toLowerCase()}`;
|
|
8457
|
+
apiRoutes.push({
|
|
8458
|
+
pattern: `${prefix}/**`,
|
|
8459
|
+
strategy: {
|
|
8460
|
+
name: "NetworkFirst",
|
|
8461
|
+
cacheName: `flask-blueprint-${blueprint.name}`,
|
|
8462
|
+
networkTimeoutSeconds: 2,
|
|
8463
|
+
expiration: {
|
|
8464
|
+
maxEntries: 30,
|
|
8465
|
+
maxAgeSeconds: 600
|
|
8466
|
+
// 10 minutes
|
|
8467
|
+
}
|
|
8468
|
+
},
|
|
8469
|
+
priority: 15,
|
|
8470
|
+
description: `Flask blueprint: ${blueprint.name}`
|
|
8471
|
+
});
|
|
8472
|
+
}
|
|
8473
|
+
}
|
|
8288
8474
|
const imageRoutes = [
|
|
8289
8475
|
{
|
|
8290
8476
|
pattern: "*.{png,jpg,jpeg,svg,webp,gif}",
|
|
@@ -8346,13 +8532,33 @@ var FlaskIntegration = class extends BaseBackendIntegration {
|
|
|
8346
8532
|
if (this.config.hasFlaskWTF) {
|
|
8347
8533
|
routes.push("/csrf-token/**");
|
|
8348
8534
|
}
|
|
8535
|
+
if (this.config.blueprints && this.config.blueprints.length > 0) {
|
|
8536
|
+
for (const blueprint of this.config.blueprints) {
|
|
8537
|
+
if (blueprint.name.toLowerCase().includes("admin") || blueprint.name.toLowerCase().includes("auth")) {
|
|
8538
|
+
const prefix = blueprint.prefix || `/${blueprint.name.toLowerCase()}`;
|
|
8539
|
+
routes.push(`${prefix}/**`);
|
|
8540
|
+
}
|
|
8541
|
+
}
|
|
8542
|
+
}
|
|
8349
8543
|
return routes;
|
|
8350
8544
|
}
|
|
8545
|
+
/**
|
|
8546
|
+
* Get detected blueprints
|
|
8547
|
+
*/
|
|
8548
|
+
getBlueprints() {
|
|
8549
|
+
return this.config.blueprints || [];
|
|
8550
|
+
}
|
|
8351
8551
|
/**
|
|
8352
8552
|
* Get API endpoint patterns
|
|
8353
8553
|
*/
|
|
8354
8554
|
getApiPatterns() {
|
|
8355
8555
|
const patterns = ["/api/**"];
|
|
8556
|
+
if (this.config.blueprints && this.config.blueprints.length > 0) {
|
|
8557
|
+
for (const blueprint of this.config.blueprints) {
|
|
8558
|
+
const prefix = blueprint.prefix || `/${blueprint.name.toLowerCase()}`;
|
|
8559
|
+
patterns.push(`${prefix}/**`);
|
|
8560
|
+
}
|
|
8561
|
+
}
|
|
8356
8562
|
if (this.config.hasFlaskRESTful) {
|
|
8357
8563
|
patterns.push("/api/v1/**", "/api/v2/**");
|
|
8358
8564
|
}
|
|
@@ -8379,13 +8585,13 @@ var FlaskIntegration = class extends BaseBackendIntegration {
|
|
|
8379
8585
|
const warnings = [];
|
|
8380
8586
|
const suggestions = [];
|
|
8381
8587
|
const projectRoot = this.config.projectRoot;
|
|
8382
|
-
if (!(0,
|
|
8588
|
+
if (!(0, import_node_fs12.existsSync)((0, import_node_path12.join)(projectRoot, "app.py")) && !(0, import_node_fs12.existsSync)((0, import_node_path12.join)(projectRoot, "application.py"))) {
|
|
8383
8589
|
warnings.push("app.py or application.py not found");
|
|
8384
8590
|
}
|
|
8385
|
-
if (!(0,
|
|
8591
|
+
if (!(0, import_node_fs12.existsSync)((0, import_node_path12.join)(projectRoot, "static"))) {
|
|
8386
8592
|
suggestions.push("Consider creating a static/ folder for static assets");
|
|
8387
8593
|
}
|
|
8388
|
-
if (!(0,
|
|
8594
|
+
if (!(0, import_node_fs12.existsSync)((0, import_node_path12.join)(projectRoot, "templates"))) {
|
|
8389
8595
|
suggestions.push(
|
|
8390
8596
|
"Consider creating a templates/ folder for Jinja2 templates"
|
|
8391
8597
|
);
|
|
@@ -8406,8 +8612,7 @@ var FlaskIntegration = class extends BaseBackendIntegration {
|
|
|
8406
8612
|
* Inject middleware code for Flask
|
|
8407
8613
|
*/
|
|
8408
8614
|
injectMiddleware() {
|
|
8409
|
-
|
|
8410
|
-
code: `# Add PWA support to Flask app
|
|
8615
|
+
let code = `# Add PWA support to Flask app
|
|
8411
8616
|
from flask import Flask, send_from_directory
|
|
8412
8617
|
|
|
8413
8618
|
app = Flask(__name__)
|
|
@@ -8419,19 +8624,42 @@ def manifest():
|
|
|
8419
8624
|
|
|
8420
8625
|
@app.route('/sw.js')
|
|
8421
8626
|
def service_worker():
|
|
8422
|
-
return send_from_directory('static', 'sw.js', mimetype='application/javascript')
|
|
8627
|
+
return send_from_directory('static', 'sw.js', mimetype='application/javascript')`;
|
|
8628
|
+
if (this.config.blueprints && this.config.blueprints.length > 0) {
|
|
8629
|
+
code += "\n\n# Register Flask blueprints\n";
|
|
8630
|
+
for (const blueprint of this.config.blueprints) {
|
|
8631
|
+
const blueprintModule = blueprint.folder.replace(/\//g, ".");
|
|
8632
|
+
code += `from ${blueprintModule} import bp as ${blueprint.name}_bp
|
|
8633
|
+
`;
|
|
8634
|
+
}
|
|
8635
|
+
code += "\n";
|
|
8636
|
+
for (const blueprint of this.config.blueprints) {
|
|
8637
|
+
const prefix = blueprint.prefix || `/${blueprint.name.toLowerCase()}`;
|
|
8638
|
+
code += `app.register_blueprint(${blueprint.name}_bp, url_prefix='${prefix}')
|
|
8639
|
+
`;
|
|
8640
|
+
}
|
|
8641
|
+
}
|
|
8642
|
+
code += `
|
|
8423
8643
|
|
|
8424
8644
|
# Ensure HTTPS in production
|
|
8425
8645
|
if __name__ == '__main__':
|
|
8426
|
-
app.run(ssl_context='adhoc' if app.debug else None)
|
|
8646
|
+
app.run(ssl_context='adhoc' if app.debug else None)`;
|
|
8647
|
+
const instructions = [
|
|
8648
|
+
"Add PWA routes to your Flask app",
|
|
8649
|
+
"Place manifest.json and sw.js in the static/ folder",
|
|
8650
|
+
"Ensure HTTPS is enabled in production",
|
|
8651
|
+
"Consider using Flask-WTF for CSRF protection"
|
|
8652
|
+
];
|
|
8653
|
+
if (this.config.blueprints && this.config.blueprints.length > 0) {
|
|
8654
|
+
instructions.push(
|
|
8655
|
+
`Register ${this.config.blueprints.length} Blueprint(s) to enable optimized caching`
|
|
8656
|
+
);
|
|
8657
|
+
}
|
|
8658
|
+
return {
|
|
8659
|
+
code,
|
|
8427
8660
|
path: "app.py",
|
|
8428
8661
|
language: "python",
|
|
8429
|
-
instructions
|
|
8430
|
-
"Add PWA routes to your Flask app",
|
|
8431
|
-
"Place manifest.json and sw.js in the static/ folder",
|
|
8432
|
-
"Ensure HTTPS is enabled in production",
|
|
8433
|
-
"Consider using Flask-WTF for CSRF protection"
|
|
8434
|
-
]
|
|
8662
|
+
instructions
|
|
8435
8663
|
};
|
|
8436
8664
|
}
|
|
8437
8665
|
};
|
|
@@ -8516,9 +8744,11 @@ function resetBackendFactory() {
|
|
|
8516
8744
|
ConfigLoadError,
|
|
8517
8745
|
ConfigValidationError,
|
|
8518
8746
|
DEFAULT_CONFIG,
|
|
8747
|
+
DEFAULT_INJECTION_EXTENSIONS,
|
|
8519
8748
|
DefaultBackendIntegrationFactory,
|
|
8520
8749
|
DjangoIntegration,
|
|
8521
8750
|
FlaskIntegration,
|
|
8751
|
+
INJECTION_EXTENSIONS_BY_PROJECT_TYPE,
|
|
8522
8752
|
IconConfigSchema,
|
|
8523
8753
|
InjectionConfigSchema,
|
|
8524
8754
|
LaravelIntegration,
|
|
@@ -8534,6 +8764,7 @@ function resetBackendFactory() {
|
|
|
8534
8764
|
SymfonyIntegration,
|
|
8535
8765
|
TelemetryCollector,
|
|
8536
8766
|
UniversalPWAConfigSchema,
|
|
8767
|
+
WORDPRESS_INJECTION_PATTERNS,
|
|
8537
8768
|
buildDependencyGraph,
|
|
8538
8769
|
checkHttps,
|
|
8539
8770
|
checkProjectHttps,
|
|
@@ -8590,6 +8821,7 @@ function resetBackendFactory() {
|
|
|
8590
8821
|
injectMetaTagsInFile,
|
|
8591
8822
|
injectMetaTagsInFilesBatch,
|
|
8592
8823
|
loadConfig,
|
|
8824
|
+
mapBackendManifestVarsToOptions,
|
|
8593
8825
|
optimizeImage,
|
|
8594
8826
|
optimizeProject,
|
|
8595
8827
|
optimizeProjectImages,
|