jcc-express-mvc 1.8.7 → 1.8.21
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/Core/index.d.ts +4 -0
- package/Core/index.d.ts.map +1 -1
- package/Core/index.js +5 -0
- package/__tests__/Job.test.d.ts +2 -0
- package/__tests__/Job.test.d.ts.map +1 -0
- package/__tests__/Job.test.js +205 -0
- package/__tests__/MySqlSchemaBlueprint.test.d.ts +2 -0
- package/__tests__/MySqlSchemaBlueprint.test.d.ts.map +1 -0
- package/__tests__/MySqlSchemaBlueprint.test.js +55 -0
- package/__tests__/PostgresTranslator.test.d.ts +2 -0
- package/__tests__/PostgresTranslator.test.d.ts.map +1 -0
- package/__tests__/PostgresTranslator.test.js +82 -0
- package/__tests__/SQLiteTranslator.test.d.ts +2 -0
- package/__tests__/SQLiteTranslator.test.d.ts.map +1 -0
- package/__tests__/SQLiteTranslator.test.js +125 -0
- package/__tests__/SchemaAlterBlueprint.test.d.ts +2 -0
- package/__tests__/SchemaAlterBlueprint.test.d.ts.map +1 -0
- package/__tests__/SchemaAlterBlueprint.test.js +35 -0
- package/global.d.ts +3 -1
- package/index.d.ts +28 -3
- package/index.d.ts.map +1 -1
- package/index.js +33 -75
- package/lib/Application/Application.d.ts.map +1 -1
- package/lib/Application/Application.js +2 -2
- package/lib/Application/ApplicationBuilder.d.ts.map +1 -1
- package/lib/Application/ApplicationBuilder.js +22 -11
- package/lib/Auth/AuthMiddleware.d.ts.map +1 -1
- package/lib/Auth/AuthMiddleware.js +29 -12
- package/lib/Auth/index.d.ts +17 -3
- package/lib/Auth/index.d.ts.map +1 -1
- package/lib/Auth/index.js +128 -26
- package/lib/Auth/loginRateLimit.d.ts +6 -0
- package/lib/Auth/loginRateLimit.d.ts.map +1 -0
- package/lib/Auth/loginRateLimit.js +25 -0
- package/lib/Auth/refreshTokenStore.d.ts +24 -0
- package/lib/Auth/refreshTokenStore.d.ts.map +1 -0
- package/lib/Auth/refreshTokenStore.js +46 -0
- package/lib/Command-Line/DBCommand.d.ts.map +1 -1
- package/lib/Command-Line/DBCommand.js +9 -3
- package/lib/Command-Line/KeyGenerateCommand.d.ts +6 -0
- package/lib/Command-Line/KeyGenerateCommand.d.ts.map +1 -0
- package/lib/Command-Line/KeyGenerateCommand.js +47 -0
- package/lib/Command-Line/MakeCommand.d.ts +1 -0
- package/lib/Command-Line/MakeCommand.d.ts.map +1 -1
- package/lib/Command-Line/MakeCommand.js +36 -32
- package/lib/Command-Line/NodeArtisanCommand.d.ts +2 -0
- package/lib/Command-Line/NodeArtisanCommand.d.ts.map +1 -1
- package/lib/Command-Line/NodeArtisanCommand.js +24 -6
- package/lib/Command-Line/NodeTinker/Tinker.d.ts +5 -2
- package/lib/Command-Line/NodeTinker/Tinker.d.ts.map +1 -1
- package/lib/Command-Line/NodeTinker/Tinker.js +13 -3
- package/lib/Command-Line/WatchCommand.d.ts +29 -0
- package/lib/Command-Line/WatchCommand.d.ts.map +1 -0
- package/lib/Command-Line/WatchCommand.js +234 -0
- package/lib/Command-Line/files/Models.d.ts.map +1 -1
- package/lib/Command-Line/files/Models.js +67 -0
- package/lib/Console/Command.d.ts +18 -0
- package/lib/Console/Command.d.ts.map +1 -1
- package/lib/Console/Command.js +29 -0
- package/lib/Container/index.d.ts.map +1 -1
- package/lib/Container/index.js +22 -8
- package/lib/Database/Database.d.ts +21 -0
- package/lib/Database/Database.d.ts.map +1 -0
- package/lib/Database/Database.js +55 -0
- package/lib/Database/DatabaseServiceProvider.d.ts +22 -0
- package/lib/Database/DatabaseServiceProvider.d.ts.map +1 -0
- package/lib/Database/DatabaseServiceProvider.js +50 -0
- package/lib/Database/Drivers/KnexDriver.d.ts +13 -0
- package/lib/Database/Drivers/KnexDriver.d.ts.map +1 -0
- package/lib/Database/Drivers/KnexDriver.js +29 -0
- package/lib/Database/Drivers/MongooseDriver.d.ts +20 -0
- package/lib/Database/Drivers/MongooseDriver.d.ts.map +1 -0
- package/lib/Database/Drivers/MongooseDriver.js +57 -0
- package/lib/Database/Drivers/SequelizeDriver.d.ts +24 -0
- package/lib/Database/Drivers/SequelizeDriver.d.ts.map +1 -0
- package/lib/Database/Drivers/SequelizeDriver.js +82 -0
- package/lib/Database/index.d.ts +7 -0
- package/lib/Database/index.d.ts.map +1 -0
- package/lib/Database/index.js +11 -0
- package/lib/Database/interface.d.ts +14 -0
- package/lib/Database/interface.d.ts.map +1 -0
- package/lib/Database/interface.js +2 -0
- package/lib/Database/type.d.ts +18 -0
- package/lib/Database/type.d.ts.map +1 -0
- package/lib/Database/type.js +2 -0
- package/lib/Error/DisplayErrorCode.d.ts.map +1 -1
- package/lib/Error/DisplayErrorCode.js +27 -17
- package/lib/Error/MissMatchTokenException/index.d.ts +4 -0
- package/lib/Error/MissMatchTokenException/index.d.ts.map +1 -0
- package/lib/Error/MissMatchTokenException/index.js +9 -0
- package/lib/Error/SocialiteAuthError/SocialiteAuthError.d.ts +9 -0
- package/lib/Error/SocialiteAuthError/SocialiteAuthError.d.ts.map +1 -0
- package/lib/Error/SocialiteAuthError/SocialiteAuthError.js +17 -0
- package/lib/Global/helpers.d.ts.map +1 -1
- package/lib/Global/helpers.js +7 -6
- package/lib/Http/index.d.ts +3 -1
- package/lib/Http/index.d.ts.map +1 -1
- package/lib/Http/index.js +26 -5
- package/lib/Interface/index.d.ts +17 -3
- package/lib/Interface/index.d.ts.map +1 -1
- package/lib/Jcc-eloquent/lib/Builder.d.ts +12 -1
- package/lib/Jcc-eloquent/lib/Builder.d.ts.map +1 -1
- package/lib/Jcc-eloquent/lib/Builder.js +119 -5
- package/lib/Jcc-eloquent/lib/Database/index.d.ts.map +1 -1
- package/lib/Jcc-eloquent/lib/Database/index.js +2 -1
- package/lib/Jcc-eloquent/lib/Interfaces/index.d.ts +37 -2
- package/lib/Jcc-eloquent/lib/Interfaces/index.d.ts.map +1 -1
- package/lib/Jcc-eloquent/lib/Migration.d.ts +6 -0
- package/lib/Jcc-eloquent/lib/Migration.d.ts.map +1 -1
- package/lib/Jcc-eloquent/lib/Migration.js +24 -2
- package/lib/Jcc-eloquent/lib/Model.d.ts +24 -7
- package/lib/Jcc-eloquent/lib/Model.d.ts.map +1 -1
- package/lib/Jcc-eloquent/lib/Model.js +35 -2
- package/lib/Jcc-eloquent/lib/QueryBuilder.d.ts +2 -1
- package/lib/Jcc-eloquent/lib/QueryBuilder.d.ts.map +1 -1
- package/lib/Jcc-eloquent/lib/QueryBuilder.js +10 -1
- package/lib/Jcc-eloquent/lib/Rollback.d.ts.map +1 -1
- package/lib/Jcc-eloquent/lib/Rollback.js +9 -2
- package/lib/Jcc-eloquent/lib/Schema/BaseSchemaEntity/index.d.ts.map +1 -1
- package/lib/Jcc-eloquent/lib/Schema/BaseSchemaEntity/index.js +3 -3
- package/lib/Jcc-eloquent/lib/Schema/BluePrint/index.d.ts +6 -2
- package/lib/Jcc-eloquent/lib/Schema/BluePrint/index.d.ts.map +1 -1
- package/lib/Jcc-eloquent/lib/Schema/BluePrint/index.js +41 -12
- package/lib/Jcc-eloquent/lib/Schema/Translator/Sqlite.d.ts.map +1 -1
- package/lib/Jcc-eloquent/lib/Schema/Translator/Sqlite.js +21 -3
- package/lib/Jcc-eloquent/lib/Schema/Translator/postgres.d.ts +1 -0
- package/lib/Jcc-eloquent/lib/Schema/Translator/postgres.d.ts.map +1 -1
- package/lib/Jcc-eloquent/lib/Schema/Translator/postgres.js +70 -14
- package/lib/Jcc-eloquent/lib/Schema/index.d.ts +12 -0
- package/lib/Jcc-eloquent/lib/Schema/index.d.ts.map +1 -1
- package/lib/Jcc-eloquent/lib/Schema/index.js +27 -6
- package/lib/Jcc-eloquent/lib/utils/index.d.ts +13 -0
- package/lib/Jcc-eloquent/lib/utils/index.d.ts.map +1 -1
- package/lib/Jcc-eloquent/lib/utils/index.js +62 -0
- package/lib/Middleware/index.d.ts +1 -0
- package/lib/Middleware/index.d.ts.map +1 -1
- package/lib/Middleware/index.js +20 -8
- package/lib/Model/Sequelize.d.ts +3 -0
- package/lib/Model/Sequelize.d.ts.map +1 -0
- package/lib/Model/Sequelize.js +6 -0
- package/lib/Providers/RouteServiceProvider.d.ts.map +1 -1
- package/lib/Providers/RouteServiceProvider.js +3 -0
- package/lib/Providers/SessionServiceProvider.d.ts +12 -0
- package/lib/Providers/SessionServiceProvider.d.ts.map +1 -0
- package/lib/Providers/SessionServiceProvider.js +47 -0
- package/lib/Queue/Controllers/QueueControllers.d.ts +31 -0
- package/lib/Queue/Controllers/QueueControllers.d.ts.map +1 -0
- package/lib/Queue/Controllers/QueueControllers.js +126 -0
- package/lib/Queue/Drivers/DatabaseDriver.d.ts +6 -1
- package/lib/Queue/Drivers/DatabaseDriver.d.ts.map +1 -1
- package/lib/Queue/Drivers/DatabaseDriver.js +76 -21
- package/lib/Queue/Drivers/MemoryDriver.d.ts +17 -6
- package/lib/Queue/Drivers/MemoryDriver.d.ts.map +1 -1
- package/lib/Queue/Drivers/MemoryDriver.js +323 -14
- package/lib/Queue/Drivers/RedisDriver.d.ts +48 -0
- package/lib/Queue/Drivers/RedisDriver.d.ts.map +1 -0
- package/lib/Queue/Drivers/RedisDriver.js +533 -0
- package/lib/Queue/Job.d.ts +8 -0
- package/lib/Queue/Job.d.ts.map +1 -1
- package/lib/Queue/Job.js +25 -1
- package/lib/Queue/JobsLogger.d.ts.map +1 -1
- package/lib/Queue/JobsLogger.js +6 -3
- package/lib/Queue/Queue.d.ts +17 -0
- package/lib/Queue/Queue.d.ts.map +1 -1
- package/lib/Queue/Queue.js +146 -11
- package/lib/Queue/QueueServiceProvider.d.ts +6 -0
- package/lib/Queue/QueueServiceProvider.d.ts.map +1 -0
- package/lib/Queue/QueueServiceProvider.js +18 -0
- package/lib/Queue/Route/web.d.ts +2 -0
- package/lib/Queue/Route/web.d.ts.map +1 -0
- package/lib/Queue/Route/web.js +16 -0
- package/lib/Queue/index.d.ts +1 -0
- package/lib/Queue/index.d.ts.map +1 -1
- package/lib/Queue/index.js +3 -0
- package/lib/Queue/interface.d.ts +25 -0
- package/lib/Queue/interface.d.ts.map +1 -1
- package/lib/Queue/middleware/queueDashboardGuard.d.ts +7 -0
- package/lib/Queue/middleware/queueDashboardGuard.d.ts.map +1 -0
- package/lib/Queue/middleware/queueDashboardGuard.js +43 -0
- package/lib/Queue/type.d.ts +12 -1
- package/lib/Queue/type.d.ts.map +1 -1
- package/lib/Response/index.d.ts.map +1 -1
- package/lib/Response/index.js +6 -1
- package/lib/Routes/Route.d.ts +2 -2
- package/lib/Routes/Route.d.ts.map +1 -1
- package/lib/Routes/RouteBuilder.d.ts +2 -2
- package/lib/Routes/RouteBuilder.d.ts.map +1 -1
- package/lib/Security/CsrfMiddleware.d.ts +37 -0
- package/lib/Security/CsrfMiddleware.d.ts.map +1 -0
- package/lib/Security/CsrfMiddleware.js +85 -0
- package/lib/Security/MethodSpoofingMiddleware.d.ts +36 -0
- package/lib/Security/MethodSpoofingMiddleware.d.ts.map +1 -0
- package/lib/Security/MethodSpoofingMiddleware.js +51 -0
- package/lib/Security/index.d.ts +5 -0
- package/lib/Security/index.d.ts.map +1 -0
- package/lib/Security/index.js +7 -0
- package/lib/Session/DatabaseSession.d.ts +9 -3
- package/lib/Session/DatabaseSession.d.ts.map +1 -1
- package/lib/Session/DatabaseSession.js +38 -36
- package/lib/Session/ExpressJccSession.d.ts +23 -0
- package/lib/Session/ExpressJccSession.d.ts.map +1 -0
- package/lib/Session/ExpressJccSession.js +110 -0
- package/lib/Session/RedisSession.d.ts +2 -2
- package/lib/Session/RedisSession.d.ts.map +1 -1
- package/lib/Session/RedisSession.js +19 -5
- package/lib/Session/SessionManager.d.ts +18 -4
- package/lib/Session/SessionManager.d.ts.map +1 -1
- package/lib/Session/SessionManager.js +53 -17
- package/lib/Socialite/AbstractProvider.d.ts +60 -0
- package/lib/Socialite/AbstractProvider.d.ts.map +1 -0
- package/lib/Socialite/AbstractProvider.js +136 -0
- package/lib/Socialite/Drivers/facebook/FacebookDriver.d.ts +9 -0
- package/lib/Socialite/Drivers/facebook/FacebookDriver.d.ts.map +1 -0
- package/lib/Socialite/Drivers/facebook/FacebookDriver.js +40 -0
- package/lib/Socialite/Drivers/facebook/FacebookProvider.d.ts +15 -0
- package/lib/Socialite/Drivers/facebook/FacebookProvider.d.ts.map +1 -0
- package/lib/Socialite/Drivers/facebook/FacebookProvider.js +28 -0
- package/lib/Socialite/Drivers/github/GitHubDriver.d.ts +9 -0
- package/lib/Socialite/Drivers/github/GitHubDriver.d.ts.map +1 -0
- package/lib/Socialite/Drivers/github/GitHubDriver.js +54 -0
- package/lib/Socialite/Drivers/github/GitHubProvider.d.ts +15 -0
- package/lib/Socialite/Drivers/github/GitHubProvider.d.ts.map +1 -0
- package/lib/Socialite/Drivers/github/GitHubProvider.js +28 -0
- package/lib/Socialite/Drivers/gitlab/GitLabDriver.d.ts +10 -0
- package/lib/Socialite/Drivers/gitlab/GitLabDriver.d.ts.map +1 -0
- package/lib/Socialite/Drivers/gitlab/GitLabDriver.js +37 -0
- package/lib/Socialite/Drivers/gitlab/GitLabProvider.d.ts +15 -0
- package/lib/Socialite/Drivers/gitlab/GitLabProvider.d.ts.map +1 -0
- package/lib/Socialite/Drivers/gitlab/GitLabProvider.js +28 -0
- package/lib/Socialite/Drivers/google/GoogleDriver.d.ts +9 -0
- package/lib/Socialite/Drivers/google/GoogleDriver.d.ts.map +1 -0
- package/lib/Socialite/Drivers/google/GoogleDriver.js +39 -0
- package/lib/Socialite/Drivers/google/GoogleProvider.d.ts +15 -0
- package/lib/Socialite/Drivers/google/GoogleProvider.d.ts.map +1 -0
- package/lib/Socialite/Drivers/google/GoogleProvider.js +28 -0
- package/lib/Socialite/Drivers/slack/SlackDriver.d.ts +9 -0
- package/lib/Socialite/Drivers/slack/SlackDriver.d.ts.map +1 -0
- package/lib/Socialite/Drivers/slack/SlackDriver.js +41 -0
- package/lib/Socialite/Drivers/slack/SlackProvider.d.ts +16 -0
- package/lib/Socialite/Drivers/slack/SlackProvider.d.ts.map +1 -0
- package/lib/Socialite/Drivers/slack/SlackProvider.js +37 -0
- package/lib/Socialite/Drivers/twitter/TwitterDriver.d.ts +9 -0
- package/lib/Socialite/Drivers/twitter/TwitterDriver.d.ts.map +1 -0
- package/lib/Socialite/Drivers/twitter/TwitterDriver.js +57 -0
- package/lib/Socialite/Drivers/twitter/TwitterProvider.d.ts +16 -0
- package/lib/Socialite/Drivers/twitter/TwitterProvider.d.ts.map +1 -0
- package/lib/Socialite/Drivers/twitter/TwitterProvider.js +38 -0
- package/lib/Socialite/SocialUser.d.ts +42 -0
- package/lib/Socialite/SocialUser.d.ts.map +1 -0
- package/lib/Socialite/SocialUser.js +116 -0
- package/lib/Socialite/Socialite.d.ts +13 -0
- package/lib/Socialite/Socialite.d.ts.map +1 -0
- package/lib/Socialite/Socialite.js +41 -0
- package/lib/Socialite/SocialiteProvider.d.ts +9 -0
- package/lib/Socialite/SocialiteProvider.d.ts.map +1 -0
- package/lib/Socialite/SocialiteProvider.js +15 -0
- package/lib/Socialite/config.d.ts +7 -0
- package/lib/Socialite/config.d.ts.map +1 -0
- package/lib/Socialite/config.js +44 -0
- package/lib/Socialite/constant.d.ts +18 -0
- package/lib/Socialite/constant.d.ts.map +1 -0
- package/lib/Socialite/constant.js +20 -0
- package/lib/Socialite/index.d.ts +13 -0
- package/lib/Socialite/index.d.ts.map +1 -0
- package/lib/Socialite/index.js +25 -0
- package/lib/Socialite/types.d.ts +13 -0
- package/lib/Socialite/types.d.ts.map +1 -0
- package/lib/Socialite/types.js +2 -0
- package/lib/Templating-engine/benchmark.d.ts +7 -0
- package/lib/Templating-engine/benchmark.d.ts.map +1 -0
- package/lib/Templating-engine/benchmark.js +44 -0
- package/lib/Templating-engine/engineHelper.d.ts +37 -8
- package/lib/Templating-engine/engineHelper.d.ts.map +1 -1
- package/lib/Templating-engine/engineHelper.js +203 -26
- package/lib/Templating-engine/errors.d.ts +15 -0
- package/lib/Templating-engine/errors.d.ts.map +1 -0
- package/lib/Templating-engine/errors.js +35 -0
- package/lib/Templating-engine/expressions.d.ts +24 -0
- package/lib/Templating-engine/expressions.d.ts.map +1 -1
- package/lib/Templating-engine/expressions.js +27 -5
- package/lib/Templating-engine/index.d.ts +87 -17
- package/lib/Templating-engine/index.d.ts.map +1 -1
- package/lib/Templating-engine/index.js +701 -96
- package/lib/Templating-engine/plugins.d.ts +30 -0
- package/lib/Templating-engine/plugins.d.ts.map +1 -0
- package/lib/Templating-engine/plugins.js +47 -0
- package/lib/Type/index.d.ts +1 -0
- package/lib/Type/index.d.ts.map +1 -1
- package/lib/Validation/Validator/CustomValidation.d.ts.map +1 -1
- package/lib/Validation/Validator/CustomValidation.js +2 -2
- package/lib/Validation/Validator/helper.d.ts.map +1 -1
- package/lib/Validation/Validator/helper.js +8 -1
- package/lib/util/index.d.ts +42 -1
- package/lib/util/index.d.ts.map +1 -1
- package/lib/util/index.js +154 -17
- package/package.json +1 -1
|
@@ -4,60 +4,196 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.compile = exports.jsBladeEngine = void 0;
|
|
7
|
+
exports.compileString = compileString;
|
|
8
|
+
exports.clearCache = clearCache;
|
|
7
9
|
const Config_1 = require("../Config/Config");
|
|
8
10
|
const engineHelper_1 = require("./engineHelper");
|
|
11
|
+
const errors_1 = require("./errors");
|
|
12
|
+
const plugins_1 = require("./plugins");
|
|
9
13
|
const fs_1 = __importDefault(require("fs"));
|
|
14
|
+
const path_1 = __importDefault(require("path"));
|
|
10
15
|
const colors_1 = __importDefault(require("colors"));
|
|
16
|
+
const app_root_path_1 = __importDefault(require("app-root-path"));
|
|
17
|
+
/** Cache for extends result (layout merge) - avoids re-reading layout on every request */
|
|
18
|
+
const extendsCache = new Map();
|
|
19
|
+
const CACHE_ENABLED = Config_1.config.get("APP_ENV") === "production";
|
|
11
20
|
class TemplatingEngine extends engineHelper_1.EngineHelpers {
|
|
12
21
|
constructor() {
|
|
13
22
|
super(...arguments);
|
|
14
23
|
this.option = {};
|
|
24
|
+
this.parseDepth = 0;
|
|
25
|
+
}
|
|
26
|
+
/** Directive dispatch - ordered by specificity (longest prefix first). */
|
|
27
|
+
getDirectiveHandler(matched) {
|
|
28
|
+
const D = [
|
|
29
|
+
["@includeUnless", this.includeUnless],
|
|
30
|
+
["@includeWhen", this.includeWhen],
|
|
31
|
+
["@includeIf", this.includeIf],
|
|
32
|
+
["@include", this.includes],
|
|
33
|
+
["@push", this.handlePush],
|
|
34
|
+
["@prepend", this.handlePrepend],
|
|
35
|
+
["@stack", this.handleStack],
|
|
36
|
+
["@production", this.handleProduction],
|
|
37
|
+
["@env", this.handleEnv],
|
|
38
|
+
["@forelse", this.handleForelse],
|
|
39
|
+
["@foreach", this.handleLoop],
|
|
40
|
+
["@for", this.handleFor],
|
|
41
|
+
["@while", this.handleWhile],
|
|
42
|
+
["@switch", this.handleSwitch],
|
|
43
|
+
["@unless", this.handleUnless],
|
|
44
|
+
["@isset", this.handleIsset],
|
|
45
|
+
["@empty", this.handleEmpty],
|
|
46
|
+
["@auth", this.auth],
|
|
47
|
+
["@guest", this.guest],
|
|
48
|
+
["@if", this.handleCondition],
|
|
49
|
+
["@ternary", this.ternary],
|
|
50
|
+
["{{--", this.handleBladeComment],
|
|
51
|
+
["{!!", this.handleVariable],
|
|
52
|
+
["{{", this.handleVariable],
|
|
53
|
+
["@json", this.handleJson],
|
|
54
|
+
["@script(", this.handleScriptPath],
|
|
55
|
+
["@script", this.handleScript],
|
|
56
|
+
["@js", this.handleJs],
|
|
57
|
+
["@inertia", this.inertia],
|
|
58
|
+
["@viteReact", this.viteReactRefresh],
|
|
59
|
+
["@vite([", this.vite],
|
|
60
|
+
["@verbatim", this.handleVerbatim],
|
|
61
|
+
["@method", this.handleMethod],
|
|
62
|
+
["@csrf", this.handleCsrf],
|
|
63
|
+
];
|
|
64
|
+
for (let i = 0; i < D.length; i++) {
|
|
65
|
+
if (matched.startsWith(D[i][0]))
|
|
66
|
+
return D[i][1];
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
/** Find leftmost directive match (avoids duplicate named groups from combined regex). */
|
|
71
|
+
findFirstMatch(content) {
|
|
72
|
+
let best = null;
|
|
73
|
+
let bestIndex = Infinity;
|
|
74
|
+
const dirs = this.directives;
|
|
75
|
+
for (let i = 0; i < TemplatingEngine.PREFIX_TO_KEY.length; i++) {
|
|
76
|
+
const [, key] = TemplatingEngine.PREFIX_TO_KEY[i];
|
|
77
|
+
const regex = dirs[key];
|
|
78
|
+
if (!regex)
|
|
79
|
+
continue;
|
|
80
|
+
const m = content.match(regex);
|
|
81
|
+
if (m && m.index !== undefined && m.index < bestIndex) {
|
|
82
|
+
bestIndex = m.index;
|
|
83
|
+
best = m;
|
|
84
|
+
if (bestIndex === 0)
|
|
85
|
+
return best;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const customPatterns = (0, plugins_1.getCustomPatterns)();
|
|
89
|
+
for (let i = 0; i < customPatterns.length; i++) {
|
|
90
|
+
const re = new RegExp(customPatterns[i]);
|
|
91
|
+
const m = content.match(re);
|
|
92
|
+
if (m && m.index !== undefined && m.index < bestIndex) {
|
|
93
|
+
bestIndex = m.index;
|
|
94
|
+
best = m;
|
|
95
|
+
if (bestIndex === 0)
|
|
96
|
+
return best;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return best;
|
|
15
100
|
}
|
|
16
101
|
/**
|
|
17
|
-
* Handles
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
102
|
+
* Handles Blade comments {{-- --}} - strips entirely from output.
|
|
103
|
+
*/
|
|
104
|
+
handleBladeComment(content, match, param) {
|
|
105
|
+
const { beforMatch, afterMatch } = this.content(content, match);
|
|
106
|
+
return this.parser(beforMatch + afterMatch, param);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Handles variable directives - {{ }} escaped (Laravel-style), {!! !!} raw.
|
|
110
|
+
* Supports JS methods: join, split, trim, toUpperCase, etc. e.g. {{ items.join(', ') }}
|
|
22
111
|
*/
|
|
23
112
|
handleVariable(content, match, param) {
|
|
24
|
-
const variableName = match.groups
|
|
25
|
-
|
|
113
|
+
const variableName = (match.groups?.param || "").trim();
|
|
114
|
+
const isRaw = match[0].startsWith("{!!");
|
|
115
|
+
if (variableName.startsWith("assets")) {
|
|
26
116
|
const assets = this.getAssets(variableName);
|
|
27
|
-
const newContent = content.
|
|
117
|
+
const newContent = content.slice(0, match.index) +
|
|
118
|
+
assets +
|
|
119
|
+
content.slice(match.index + match[0].length);
|
|
28
120
|
return this.parser(newContent, param);
|
|
29
121
|
}
|
|
30
|
-
const variableValue = this.
|
|
31
|
-
const
|
|
122
|
+
const variableValue = this.resolveVariableExpression(variableName, param);
|
|
123
|
+
const output = isRaw || variableValue === ""
|
|
124
|
+
? String(variableValue ?? "")
|
|
125
|
+
: this.htmlspecialchars(variableValue);
|
|
126
|
+
const newContent = content.slice(0, match.index) +
|
|
127
|
+
output +
|
|
128
|
+
content.slice(match.index + match[0].length);
|
|
32
129
|
return this.parser(newContent, param);
|
|
33
130
|
}
|
|
34
131
|
/**
|
|
35
|
-
* Handles the @foreach directive
|
|
36
|
-
* @param {string} content - Template content.
|
|
37
|
-
* @param {Object} match - Match object from the regular expression.
|
|
38
|
-
* @param {Object} param - Local variables for rendering.
|
|
39
|
-
* @returns {string} - Rendered content.
|
|
132
|
+
* Handles the @foreach directive (Laravel-style with $loop variable).
|
|
40
133
|
*/
|
|
41
134
|
handleLoop(content, match, param) {
|
|
42
135
|
let result = "";
|
|
43
|
-
const loopData = this.accessNestedProperty(param, match?.groups
|
|
44
|
-
|
|
136
|
+
const loopData = this.accessNestedProperty(param, match?.groups?.loopData?.trim());
|
|
137
|
+
const loopVar = match?.groups?.loopvariable?.trim();
|
|
138
|
+
if (Array.isArray(loopData) && loopVar) {
|
|
45
139
|
const loopBody = match.groups.loopBody;
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
140
|
+
const count = loopData.length;
|
|
141
|
+
const parts = [];
|
|
142
|
+
for (let index = 0; index < count; index++) {
|
|
143
|
+
const item = loopData[index];
|
|
144
|
+
const loopContext = Object.assign({}, param, {
|
|
145
|
+
[loopVar]: item,
|
|
146
|
+
loop: {
|
|
147
|
+
index,
|
|
148
|
+
first: index === 0,
|
|
149
|
+
last: index === count - 1,
|
|
150
|
+
count,
|
|
151
|
+
even: index % 2 === 1,
|
|
152
|
+
odd: index % 2 === 0,
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
Object.defineProperty(loopContext[loopVar], "loopIndex", {
|
|
156
|
+
value: index,
|
|
157
|
+
enumerable: true,
|
|
158
|
+
});
|
|
159
|
+
parts.push(this.parser(loopBody + "@endforeach", loopContext));
|
|
160
|
+
}
|
|
161
|
+
result = parts.join("");
|
|
56
162
|
}
|
|
57
163
|
const { afterMatch, beforMatch } = this.content(content, match);
|
|
58
164
|
const afterListResult = this.parser(afterMatch, param);
|
|
59
|
-
|
|
60
|
-
|
|
165
|
+
return (beforMatch + result + afterListResult).replace(/@endforeach/gm, "");
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Handles @forelse - foreach with @empty fallback (Laravel-style).
|
|
169
|
+
*/
|
|
170
|
+
handleForelse(content, match, param) {
|
|
171
|
+
const loopData = this.accessNestedProperty(param, match?.groups?.loopData?.trim());
|
|
172
|
+
const loopVar = match?.groups?.loopvariable?.trim();
|
|
173
|
+
const { afterMatch, beforMatch } = this.content(content, match);
|
|
174
|
+
if (Array.isArray(loopData) && loopData.length > 0 && loopVar) {
|
|
175
|
+
const loopBody = match.groups.loopBody;
|
|
176
|
+
const count = loopData.length;
|
|
177
|
+
const parts = [];
|
|
178
|
+
for (let index = 0; index < count; index++) {
|
|
179
|
+
const item = loopData[index];
|
|
180
|
+
const loopContext = Object.assign({}, param, {
|
|
181
|
+
[loopVar]: item,
|
|
182
|
+
loop: {
|
|
183
|
+
index,
|
|
184
|
+
first: index === 0,
|
|
185
|
+
last: index === count - 1,
|
|
186
|
+
count,
|
|
187
|
+
even: index % 2 === 1,
|
|
188
|
+
odd: index % 2 === 0,
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
parts.push(this.parser(loopBody, loopContext));
|
|
192
|
+
}
|
|
193
|
+
return this.parser(beforMatch + parts.join("") + afterMatch, param);
|
|
194
|
+
}
|
|
195
|
+
const emptyBody = match.groups?.emptyBody || "";
|
|
196
|
+
return this.parser(beforMatch + emptyBody + afterMatch, param);
|
|
61
197
|
}
|
|
62
198
|
/**
|
|
63
199
|
* Handles the @if directive in the template content.
|
|
@@ -66,16 +202,41 @@ class TemplatingEngine extends engineHelper_1.EngineHelpers {
|
|
|
66
202
|
* @param {Object} param - Local variables for rendering.
|
|
67
203
|
* @returns {string} - Rendered content.
|
|
68
204
|
*/
|
|
205
|
+
/**
|
|
206
|
+
* Handles @if with @elseif and @else (Laravel-style).
|
|
207
|
+
*/
|
|
69
208
|
handleCondition(content, match, param) {
|
|
209
|
+
const ifData = (match?.groups?.ifData || "").trim();
|
|
210
|
+
const fullBody = match?.groups?.ifBody || "";
|
|
70
211
|
let result = "";
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
212
|
+
const parts = [];
|
|
213
|
+
let pos = 0;
|
|
214
|
+
const re = /@elseif\s*\(([^)]+)\)\s*|@else\s*/g;
|
|
215
|
+
let m;
|
|
216
|
+
parts.push({ type: "if", cond: ifData, body: "" });
|
|
217
|
+
while ((m = re.exec(fullBody)) !== null) {
|
|
218
|
+
parts[parts.length - 1].body = fullBody.slice(pos, m.index).trim();
|
|
219
|
+
if (m[0].startsWith("@elseif")) {
|
|
220
|
+
parts.push({ type: "elseif", cond: m[1].trim(), body: "" });
|
|
221
|
+
pos = m.index + m[0].length;
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
parts.push({
|
|
225
|
+
type: "else",
|
|
226
|
+
body: fullBody.slice(m.index + m[0].length).trim(),
|
|
227
|
+
});
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
75
230
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
231
|
+
if (parts[parts.length - 1].body === "" &&
|
|
232
|
+
parts[parts.length - 1].type !== "else") {
|
|
233
|
+
parts[parts.length - 1].body = fullBody.slice(pos).trim();
|
|
234
|
+
}
|
|
235
|
+
for (const part of parts) {
|
|
236
|
+
if (part.type === "else" ||
|
|
237
|
+
(part.cond && this.evaluateCondition(part.cond, param))) {
|
|
238
|
+
result = this.parser(part.body, param);
|
|
239
|
+
break;
|
|
79
240
|
}
|
|
80
241
|
}
|
|
81
242
|
const { beforMatch, afterMatch } = this.content(content, match);
|
|
@@ -89,14 +250,11 @@ class TemplatingEngine extends engineHelper_1.EngineHelpers {
|
|
|
89
250
|
* @returns {string} - Rendered content.
|
|
90
251
|
*/
|
|
91
252
|
ternary(content, match, param) {
|
|
92
|
-
|
|
93
|
-
const value = this.evaluateCondition(
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
else {
|
|
98
|
-
result = this.parser(match.groups?.falseData, param);
|
|
99
|
-
}
|
|
253
|
+
const condition = (match?.groups?.condition || "").trim();
|
|
254
|
+
const value = this.evaluateCondition(condition, param);
|
|
255
|
+
const result = value
|
|
256
|
+
? this.parser(match.groups?.trueData ?? "", param)
|
|
257
|
+
: this.parser(match.groups?.falseData ?? "", param);
|
|
100
258
|
const { beforMatch, afterMatch } = this.content(content, match);
|
|
101
259
|
return beforMatch + result + this.parser(afterMatch, param);
|
|
102
260
|
}
|
|
@@ -130,11 +288,232 @@ class TemplatingEngine extends engineHelper_1.EngineHelpers {
|
|
|
130
288
|
if (includePath) {
|
|
131
289
|
const includeContent = this.readFile(`views/${includePath}`);
|
|
132
290
|
const { afterMatch, beforMatch } = this.content(content, match);
|
|
291
|
+
const extraData = this.parseIncludeData(match?.groups?.includeData, param);
|
|
292
|
+
const mergedParam = extraData ? { ...param, ...extraData } : param;
|
|
133
293
|
const remainStr = this.parser(afterMatch, param);
|
|
134
|
-
return this.parser(beforMatch + includeContent + remainStr,
|
|
294
|
+
return this.parser(beforMatch + includeContent + remainStr, mergedParam);
|
|
135
295
|
}
|
|
136
296
|
return content;
|
|
137
297
|
}
|
|
298
|
+
/**
|
|
299
|
+
* Parses the inline data object from @include("path", { key: value }).
|
|
300
|
+
* Supports string literals, numbers, booleans, and variable references (including dot notation).
|
|
301
|
+
* String concatenation with + is supported (e.g. "/path/" + job.id).
|
|
302
|
+
*/
|
|
303
|
+
parseIncludeData(dataStr, param) {
|
|
304
|
+
if (!dataStr)
|
|
305
|
+
return null;
|
|
306
|
+
const inner = dataStr.trim().slice(1, -1).trim();
|
|
307
|
+
if (!inner)
|
|
308
|
+
return null;
|
|
309
|
+
const result = {};
|
|
310
|
+
const pairs = this.splitIncludeArgs(inner);
|
|
311
|
+
for (const pair of pairs) {
|
|
312
|
+
const colonIdx = pair.indexOf(":");
|
|
313
|
+
if (colonIdx === -1)
|
|
314
|
+
continue;
|
|
315
|
+
const key = pair.slice(0, colonIdx).trim();
|
|
316
|
+
const rawValue = pair.slice(colonIdx + 1).trim();
|
|
317
|
+
result[key] = this.resolveIncludeValue(rawValue, param);
|
|
318
|
+
}
|
|
319
|
+
return Object.keys(result).length > 0 ? result : null;
|
|
320
|
+
}
|
|
321
|
+
/** Split key:value pairs by comma, respecting quoted strings. */
|
|
322
|
+
splitIncludeArgs(str) {
|
|
323
|
+
const parts = [];
|
|
324
|
+
let current = "";
|
|
325
|
+
let depth = 0;
|
|
326
|
+
let inQuote = "";
|
|
327
|
+
for (let i = 0; i < str.length; i++) {
|
|
328
|
+
const c = str[i];
|
|
329
|
+
if (inQuote) {
|
|
330
|
+
current += c;
|
|
331
|
+
if (c === inQuote && str[i - 1] !== "\\")
|
|
332
|
+
inQuote = "";
|
|
333
|
+
}
|
|
334
|
+
else if (c === '"' || c === "'") {
|
|
335
|
+
current += c;
|
|
336
|
+
inQuote = c;
|
|
337
|
+
}
|
|
338
|
+
else if (c === "{") {
|
|
339
|
+
current += c;
|
|
340
|
+
depth++;
|
|
341
|
+
}
|
|
342
|
+
else if (c === "}") {
|
|
343
|
+
current += c;
|
|
344
|
+
depth--;
|
|
345
|
+
}
|
|
346
|
+
else if (c === "," && depth === 0) {
|
|
347
|
+
parts.push(current.trim());
|
|
348
|
+
current = "";
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
current += c;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
if (current.trim())
|
|
355
|
+
parts.push(current.trim());
|
|
356
|
+
return parts;
|
|
357
|
+
}
|
|
358
|
+
/** Resolve a value expression: quoted string, number, boolean, concatenation, or variable. */
|
|
359
|
+
resolveIncludeValue(raw, param) {
|
|
360
|
+
if (/^\d+(\.\d+)?$/.test(raw))
|
|
361
|
+
return Number(raw);
|
|
362
|
+
if (raw === "true")
|
|
363
|
+
return true;
|
|
364
|
+
if (raw === "false")
|
|
365
|
+
return false;
|
|
366
|
+
if (raw === "null")
|
|
367
|
+
return null;
|
|
368
|
+
if (raw.includes("+")) {
|
|
369
|
+
const segments = raw.split("+").map((s) => s.trim());
|
|
370
|
+
return segments
|
|
371
|
+
.map((seg) => this.resolveIncludeValue(seg, param))
|
|
372
|
+
.join("");
|
|
373
|
+
}
|
|
374
|
+
const qm = raw.match(/^(['"])(.*)\1$/);
|
|
375
|
+
if (qm)
|
|
376
|
+
return qm[2];
|
|
377
|
+
return this.accessNestedProperty(param, raw);
|
|
378
|
+
}
|
|
379
|
+
includeIf(content, match, param) {
|
|
380
|
+
const viewPath = this.removeQuotes(match?.groups?.incIfPath || "").trim();
|
|
381
|
+
if (!viewPath || !this.viewExists(`views/${viewPath}`)) {
|
|
382
|
+
const { afterMatch, beforMatch } = this.content(content, match);
|
|
383
|
+
return this.parser(beforMatch + afterMatch, param);
|
|
384
|
+
}
|
|
385
|
+
const includeContent = this.readFile(`views/${viewPath}`);
|
|
386
|
+
const { afterMatch, beforMatch } = this.content(content, match);
|
|
387
|
+
return this.parser(beforMatch + includeContent + afterMatch, param);
|
|
388
|
+
}
|
|
389
|
+
includeWhen(content, match, param) {
|
|
390
|
+
const cond = (match?.groups?.incWhenCond || "").trim();
|
|
391
|
+
const path = this.removeQuotes(match?.groups?.incWhenPath || "").trim();
|
|
392
|
+
const { afterMatch, beforMatch } = this.content(content, match);
|
|
393
|
+
if (!this.evaluateCondition(cond, param)) {
|
|
394
|
+
return this.parser(beforMatch + afterMatch, param);
|
|
395
|
+
}
|
|
396
|
+
const includeContent = this.readFile(`views/${path}`);
|
|
397
|
+
return this.parser(beforMatch + includeContent + afterMatch, param);
|
|
398
|
+
}
|
|
399
|
+
includeUnless(content, match, param) {
|
|
400
|
+
const cond = (match?.groups?.incUnlessCond || "").trim();
|
|
401
|
+
const path = this.removeQuotes(match?.groups?.incUnlessPath || "").trim();
|
|
402
|
+
const { afterMatch, beforMatch } = this.content(content, match);
|
|
403
|
+
if (this.evaluateCondition(cond, param)) {
|
|
404
|
+
return this.parser(beforMatch + afterMatch, param);
|
|
405
|
+
}
|
|
406
|
+
const includeContent = this.readFile(`views/${path}`);
|
|
407
|
+
return this.parser(beforMatch + includeContent + afterMatch, param);
|
|
408
|
+
}
|
|
409
|
+
handleFor(content, match, param) {
|
|
410
|
+
const varName = match?.groups?.forVar?.trim();
|
|
411
|
+
const start = parseInt(match?.groups?.forStart ?? "0", 10);
|
|
412
|
+
const end = parseInt(match?.groups?.forEnd ?? "0", 10);
|
|
413
|
+
const step = parseInt(match?.groups?.forStep ?? "1", 10);
|
|
414
|
+
const body = match?.groups?.forBody ?? "";
|
|
415
|
+
let result = "";
|
|
416
|
+
for (let i = start; step > 0 ? i < end : i > end; i += step) {
|
|
417
|
+
const ctx = { ...param, [varName]: i };
|
|
418
|
+
result += this.parser(body, ctx);
|
|
419
|
+
}
|
|
420
|
+
const { afterMatch, beforMatch } = this.content(content, match);
|
|
421
|
+
return this.parser(beforMatch + result + afterMatch, param);
|
|
422
|
+
}
|
|
423
|
+
handleWhile(content, match, param) {
|
|
424
|
+
const cond = (match?.groups?.whileCond || "").trim();
|
|
425
|
+
const body = match?.groups?.whileBody ?? "";
|
|
426
|
+
let result = "";
|
|
427
|
+
let iterations = 0;
|
|
428
|
+
const maxIterations = 10000;
|
|
429
|
+
while (this.evaluateCondition(cond, param) &&
|
|
430
|
+
iterations++ < maxIterations) {
|
|
431
|
+
result += this.parser(body, param);
|
|
432
|
+
}
|
|
433
|
+
const { afterMatch, beforMatch } = this.content(content, match);
|
|
434
|
+
return this.parser(beforMatch + result + afterMatch, param);
|
|
435
|
+
}
|
|
436
|
+
handleSwitch(content, match, param) {
|
|
437
|
+
const switchVar = (match?.groups?.switchVar || "").trim();
|
|
438
|
+
const body = match?.groups?.switchBody ?? "";
|
|
439
|
+
const switchVal = this.accessNestedProperty(param, switchVar);
|
|
440
|
+
const caseRe = /@case\s*\(([^)]+)\)|@default\b/g;
|
|
441
|
+
const cases = [];
|
|
442
|
+
let pos = 0;
|
|
443
|
+
let m;
|
|
444
|
+
while ((m = caseRe.exec(body)) !== null) {
|
|
445
|
+
if (cases.length)
|
|
446
|
+
cases[cases.length - 1].body = body
|
|
447
|
+
.slice(pos, m.index)
|
|
448
|
+
.replace(/@break\b/g, "");
|
|
449
|
+
if (m[0].startsWith("@case")) {
|
|
450
|
+
cases.push({
|
|
451
|
+
type: "case",
|
|
452
|
+
value: this.removeQuotes(m[1]).trim(),
|
|
453
|
+
body: "",
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
cases.push({ type: "default", body: "" });
|
|
458
|
+
}
|
|
459
|
+
pos = m.index + m[0].length;
|
|
460
|
+
}
|
|
461
|
+
if (cases.length)
|
|
462
|
+
cases[cases.length - 1].body = body.slice(pos).replace(/@break\b/g, "");
|
|
463
|
+
let result = "";
|
|
464
|
+
for (const c of cases) {
|
|
465
|
+
if (c.type === "default" ||
|
|
466
|
+
String(this.removeQuotes(c.value)) === String(switchVal)) {
|
|
467
|
+
result = this.parser(c.body, param);
|
|
468
|
+
break;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
const { afterMatch, beforMatch } = this.content(content, match);
|
|
472
|
+
return this.parser(beforMatch + result + afterMatch, param);
|
|
473
|
+
}
|
|
474
|
+
handleProduction(content, match, param) {
|
|
475
|
+
const body = match?.groups?.prodBody ?? "";
|
|
476
|
+
const { afterMatch, beforMatch } = this.content(content, match);
|
|
477
|
+
const result = Config_1.config.get("APP_ENV") === "production" ? this.parser(body, param) : "";
|
|
478
|
+
return this.parser(beforMatch + result + afterMatch, param);
|
|
479
|
+
}
|
|
480
|
+
handleEnv(content, match, param) {
|
|
481
|
+
const envNames = (match?.groups?.envNames || "")
|
|
482
|
+
.split(",")
|
|
483
|
+
.map((s) => this.removeQuotes(s.trim()));
|
|
484
|
+
const body = match?.groups?.envBody ?? "";
|
|
485
|
+
const currentEnv = Config_1.config.get("APP_ENV") || "";
|
|
486
|
+
const matchEnv = envNames.some((e) => e === currentEnv);
|
|
487
|
+
const { afterMatch, beforMatch } = this.content(content, match);
|
|
488
|
+
const result = matchEnv ? this.parser(body, param) : "";
|
|
489
|
+
return this.parser(beforMatch + result + afterMatch, param);
|
|
490
|
+
}
|
|
491
|
+
handlePush(content, match, param) {
|
|
492
|
+
const name = this.removeQuotes(match?.groups?.pushName || "").trim();
|
|
493
|
+
const body = match?.groups?.pushBody ?? "";
|
|
494
|
+
if (!param.__bladeStacks)
|
|
495
|
+
param.__bladeStacks = {};
|
|
496
|
+
param.__bladeStacks[name] =
|
|
497
|
+
(param.__bladeStacks[name] || "") + this.parser(body, param);
|
|
498
|
+
const { afterMatch, beforMatch } = this.content(content, match);
|
|
499
|
+
return this.parser(beforMatch + afterMatch, param);
|
|
500
|
+
}
|
|
501
|
+
handlePrepend(content, match, param) {
|
|
502
|
+
const name = this.removeQuotes(match?.groups?.prependName || "").trim();
|
|
503
|
+
const body = match?.groups?.prependBody ?? "";
|
|
504
|
+
if (!param.__bladeStacks)
|
|
505
|
+
param.__bladeStacks = {};
|
|
506
|
+
param.__bladeStacks[name] =
|
|
507
|
+
this.parser(body, param) + (param.__bladeStacks[name] || "");
|
|
508
|
+
const { afterMatch, beforMatch } = this.content(content, match);
|
|
509
|
+
return this.parser(beforMatch + afterMatch, param);
|
|
510
|
+
}
|
|
511
|
+
handleStack(content, match, param) {
|
|
512
|
+
const name = this.removeQuotes(match?.groups?.stackName || "").trim();
|
|
513
|
+
const stackContent = param?.__bladeStacks?.[name] || "";
|
|
514
|
+
const { afterMatch, beforMatch } = this.content(content, match);
|
|
515
|
+
return this.parser(beforMatch + stackContent + afterMatch, param);
|
|
516
|
+
}
|
|
138
517
|
/**
|
|
139
518
|
* Handles the @auth directive in the template content.
|
|
140
519
|
* @param {string} content - Template content.
|
|
@@ -144,15 +523,15 @@ class TemplatingEngine extends engineHelper_1.EngineHelpers {
|
|
|
144
523
|
*/
|
|
145
524
|
auth(content, match, param) {
|
|
146
525
|
let result = "";
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
526
|
+
const auth = param?.Auth;
|
|
527
|
+
const isAuthenticated = auth && typeof auth === "object" && Object.keys(auth).length > 0;
|
|
528
|
+
if (isAuthenticated) {
|
|
529
|
+
const authData = match?.groups?.authData?.replace(/(\'|\")/g, "").trim();
|
|
530
|
+
if (!authData ||
|
|
531
|
+
param?.Auth?.role === authData ||
|
|
150
532
|
param?.Auth?.roleType === authData ||
|
|
151
533
|
param?.Auth?.isAdmin) {
|
|
152
|
-
result = match?.groups?.authBody;
|
|
153
|
-
}
|
|
154
|
-
if (!authData) {
|
|
155
|
-
result = match?.groups?.authBody;
|
|
534
|
+
result = match?.groups?.authBody ?? "";
|
|
156
535
|
}
|
|
157
536
|
}
|
|
158
537
|
const { beforMatch, afterMatch } = this.content(content, match);
|
|
@@ -196,77 +575,303 @@ class TemplatingEngine extends engineHelper_1.EngineHelpers {
|
|
|
196
575
|
const { beforMatch, afterMatch } = this.content(content, match);
|
|
197
576
|
let viteClientRefresh = "";
|
|
198
577
|
if (Config_1.config.get("APP_ENV") != "production") {
|
|
199
|
-
viteClientRefresh = this.
|
|
578
|
+
viteClientRefresh = this.injectReactRefresh();
|
|
200
579
|
}
|
|
201
580
|
return this.parser(beforMatch + viteClientRefresh + afterMatch);
|
|
202
581
|
}
|
|
203
582
|
/**
|
|
204
583
|
* Parses the template content based on the defined directives.
|
|
205
|
-
*
|
|
206
|
-
* @param {Object} param - Local variables for rendering.
|
|
207
|
-
* @returns {string} - Rendered content.
|
|
584
|
+
* Uses pre-compiled regex for performance.
|
|
208
585
|
*/
|
|
209
586
|
parser(content, param = this.option) {
|
|
210
|
-
|
|
211
|
-
.
|
|
212
|
-
.
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
return this.includes(content, match, param);
|
|
218
|
-
case match[0].startsWith("@foreach"):
|
|
219
|
-
return this.handleLoop(content, match, param);
|
|
220
|
-
case match[0].startsWith("@auth"):
|
|
221
|
-
return this.auth(content, match, param);
|
|
222
|
-
case match[0].startsWith("@guest"):
|
|
223
|
-
return this.guest(content, match, param);
|
|
224
|
-
case match[0].startsWith("@if"):
|
|
225
|
-
return this.handleCondition(content, match, param);
|
|
226
|
-
case match[0].startsWith("@ternary"):
|
|
227
|
-
return this.ternary(content, match, param);
|
|
228
|
-
case match[0].startsWith("{{"):
|
|
229
|
-
return this.handleVariable(content, match, param);
|
|
230
|
-
case match[0].startsWith("@inertia"):
|
|
231
|
-
return this.inertia(content, match, param);
|
|
232
|
-
case match[0].startsWith("@viteReact"):
|
|
233
|
-
return this.viteReactRefresh(content, match, param);
|
|
234
|
-
case match[0].startsWith("@vite(["):
|
|
235
|
-
return this.vite(content, match, param);
|
|
236
|
-
default:
|
|
587
|
+
if (++this.parseDepth > TemplatingEngine.MAX_PARSE_DEPTH) {
|
|
588
|
+
this.parseDepth = 0;
|
|
589
|
+
throw new errors_1.TemplateError("Maximum parse depth exceeded - possible circular include or infinite recursion");
|
|
590
|
+
}
|
|
591
|
+
try {
|
|
592
|
+
const match = this.findFirstMatch(content);
|
|
593
|
+
if (!match)
|
|
237
594
|
return content;
|
|
595
|
+
const matched = match[0];
|
|
596
|
+
const custom = (0, plugins_1.findCustomDirective)(matched, match);
|
|
597
|
+
if (custom) {
|
|
598
|
+
return custom.handler(content, match, param, (c, p) => this.parser(c, p));
|
|
599
|
+
}
|
|
600
|
+
const handler = this.getDirectiveHandler(matched);
|
|
601
|
+
if (handler)
|
|
602
|
+
return handler.call(this, content, match, param);
|
|
603
|
+
return content;
|
|
604
|
+
}
|
|
605
|
+
finally {
|
|
606
|
+
this.parseDepth--;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
handleVerbatim(content, match, param) {
|
|
610
|
+
const verbatimBody = match?.groups?.verbatimBody ?? "";
|
|
611
|
+
const { beforMatch, afterMatch } = this.content(content, match);
|
|
612
|
+
return beforMatch + verbatimBody + this.parser(afterMatch, param);
|
|
613
|
+
}
|
|
614
|
+
handleUnless(content, match, param) {
|
|
615
|
+
const cond = (match?.groups?.unlessData || "").trim();
|
|
616
|
+
const body = match?.groups?.unlessBody ?? "";
|
|
617
|
+
const { beforMatch, afterMatch } = this.content(content, match);
|
|
618
|
+
const result = this.evaluateCondition(cond, param)
|
|
619
|
+
? ""
|
|
620
|
+
: this.parser(body, param);
|
|
621
|
+
return this.parser(beforMatch + result + afterMatch, param);
|
|
622
|
+
}
|
|
623
|
+
handleIsset(content, match, param) {
|
|
624
|
+
const key = (match?.groups?.issetData || "").trim();
|
|
625
|
+
const body = match?.groups?.issetBody ?? "";
|
|
626
|
+
const isSet = this.hasNestedProperty(param, key);
|
|
627
|
+
const { beforMatch, afterMatch } = this.content(content, match);
|
|
628
|
+
const result = isSet ? this.parser(body, param) : "";
|
|
629
|
+
return this.parser(beforMatch + result + afterMatch, param);
|
|
630
|
+
}
|
|
631
|
+
handleEmpty(content, match, param) {
|
|
632
|
+
const key = (match?.groups?.emptyData || "").trim();
|
|
633
|
+
const body = match?.groups?.emptyBody ?? "";
|
|
634
|
+
const val = this.accessNestedProperty(param, key);
|
|
635
|
+
const isEmpty = val === "" ||
|
|
636
|
+
val === undefined ||
|
|
637
|
+
val === null ||
|
|
638
|
+
val === false ||
|
|
639
|
+
val === 0 ||
|
|
640
|
+
val === "0" ||
|
|
641
|
+
(Array.isArray(val) && val.length === 0) ||
|
|
642
|
+
(typeof val === "object" &&
|
|
643
|
+
val !== null &&
|
|
644
|
+
Object.keys(val).length === 0);
|
|
645
|
+
const { beforMatch, afterMatch } = this.content(content, match);
|
|
646
|
+
const result = isEmpty ? this.parser(body, param) : "";
|
|
647
|
+
return this.parser(beforMatch + result + afterMatch, param);
|
|
648
|
+
}
|
|
649
|
+
handleJson(content, match, param) {
|
|
650
|
+
const key = (match?.groups?.jsonParam || "").trim();
|
|
651
|
+
const val = this.accessNestedProperty(param, key);
|
|
652
|
+
const json = JSON.stringify(val ?? null);
|
|
653
|
+
const { beforMatch, afterMatch } = this.content(content, match);
|
|
654
|
+
return this.parser(beforMatch + this.escapeJsonForHtml(json) + afterMatch, param);
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* Handles @csrf - outputs a hidden input with the CSRF token from res.locals._token.
|
|
658
|
+
*/
|
|
659
|
+
handleCsrf(content, match, param) {
|
|
660
|
+
const token = param?._token ?? "";
|
|
661
|
+
const hidden = `<input type="hidden" name="_token" value="${token}">`;
|
|
662
|
+
const { beforMatch, afterMatch } = this.content(content, match);
|
|
663
|
+
return this.parser(beforMatch + hidden + afterMatch, param);
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* Handles @method('PUT') or @method(variable) - outputs a hidden input for HTTP method spoofing.
|
|
667
|
+
*/
|
|
668
|
+
handleMethod(content, match, param) {
|
|
669
|
+
const raw = (match?.groups?.methodValue ?? "").trim();
|
|
670
|
+
const qm = raw.match(/^(['"])(.*)\1$/);
|
|
671
|
+
const method = (qm ? qm[2] : String(this.accessNestedProperty(param, raw))).toUpperCase();
|
|
672
|
+
const hidden = `<input type="hidden" name="_method" value="${method}">`;
|
|
673
|
+
const { beforMatch, afterMatch } = this.content(content, match);
|
|
674
|
+
return this.parser(beforMatch + hidden + afterMatch, param);
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Handles @js ... @endjs - executes JavaScript on the server only.
|
|
678
|
+
* Variables and functions declared (const/let/var) are merged into template scope for use in {{ }}.
|
|
679
|
+
* Strip const/let/var so assignments go to sandbox (param) - var/undeclared assign to global.
|
|
680
|
+
*/
|
|
681
|
+
handleJs(content, match, param) {
|
|
682
|
+
const jsBody = (match?.groups?.jsBody ?? "").trim();
|
|
683
|
+
try {
|
|
684
|
+
const vm = require("node:vm");
|
|
685
|
+
const sandbox = { ...param };
|
|
686
|
+
let stripped = jsBody.replace(/\b(const|let|var)\s+/g, " ");
|
|
687
|
+
stripped = stripped.replace(/([;}\d"'`])\s*(\w+\s*=)/g, "$1; $2");
|
|
688
|
+
vm.runInNewContext(stripped, sandbox);
|
|
689
|
+
Object.assign(param, sandbox);
|
|
690
|
+
}
|
|
691
|
+
catch (err) {
|
|
692
|
+
console.error("[TemplatingEngine] @js execution error:", err?.message);
|
|
238
693
|
}
|
|
694
|
+
const { beforMatch, afterMatch } = this.content(content, match);
|
|
695
|
+
return this.parser(beforMatch + afterMatch, param);
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* Handles @script ... @endscript - outputs content as a script block for client-side.
|
|
699
|
+
* Body is parsed for {{ }} so you can inject template variables.
|
|
700
|
+
*/
|
|
701
|
+
handleScript(content, match, param) {
|
|
702
|
+
const scriptBody = (match?.groups?.scriptBody ?? "").trim();
|
|
703
|
+
const parsed = this.parser(scriptBody, param);
|
|
704
|
+
const { beforMatch, afterMatch } = this.content(content, match);
|
|
705
|
+
return this.parser(beforMatch + "<script>" + parsed + "</script>" + afterMatch, param);
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* Handles @script('path') - includes a script from resources/js.
|
|
709
|
+
* Path is the filename (e.g. 'main' -> resources/js/main.jsx)
|
|
710
|
+
*/
|
|
711
|
+
handleScriptPath(content, match, param) {
|
|
712
|
+
const scriptPath = (match?.groups?.scriptPath ?? "").trim();
|
|
713
|
+
const ext = scriptPath.includes(".") ? "" : ".jsx";
|
|
714
|
+
const resolvePath = `resources/js/${scriptPath.replace(/\./g, "/")}${ext}`;
|
|
715
|
+
const { beforMatch, afterMatch } = this.content(content, match);
|
|
716
|
+
return this.parser(this.getAssetPath(resolvePath, beforMatch, afterMatch), param);
|
|
239
717
|
}
|
|
240
718
|
/**
|
|
241
719
|
* Compiles the template content by processing directives and extending layouts.
|
|
242
|
-
*
|
|
243
|
-
* @returns {Promise<string>} - Compiled content.
|
|
720
|
+
* Caches extends result in production for performance.
|
|
244
721
|
*/
|
|
245
|
-
async compiler(content) {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
722
|
+
async compiler(content, cacheKey) {
|
|
723
|
+
let newContent;
|
|
724
|
+
if (CACHE_ENABLED && cacheKey) {
|
|
725
|
+
const cached = extendsCache.get(cacheKey);
|
|
726
|
+
if (cached !== undefined) {
|
|
727
|
+
newContent = cached;
|
|
728
|
+
}
|
|
729
|
+
else {
|
|
730
|
+
newContent = await this.extends(content);
|
|
731
|
+
extendsCache.set(cacheKey, newContent);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
else {
|
|
735
|
+
newContent = await this.extends(content);
|
|
736
|
+
}
|
|
737
|
+
newContent = this.collectBladeStacks(newContent, this.option);
|
|
738
|
+
return this.parser(newContent);
|
|
739
|
+
}
|
|
740
|
+
getExtendsCacheKey(content, resolvedPath) {
|
|
741
|
+
if (!CACHE_ENABLED)
|
|
742
|
+
return null;
|
|
743
|
+
const stat = fs_1.default.statSync(resolvedPath);
|
|
744
|
+
let key = `${resolvedPath}:${stat.mtimeMs}`;
|
|
745
|
+
const extendsMatch = content.match(this.directives.extends);
|
|
746
|
+
if (extendsMatch?.groups?.extendPath) {
|
|
747
|
+
const layoutDotPath = this.removeQuotes(extendsMatch.groups.extendPath).replace(/\./g, "/");
|
|
748
|
+
const layoutPath = path_1.default.join(app_root_path_1.default.path, "resources", "views", layoutDotPath + ".blade.html");
|
|
749
|
+
if (fs_1.default.existsSync(layoutPath)) {
|
|
750
|
+
key += `:${layoutPath}:${fs_1.default.statSync(layoutPath).mtimeMs}`;
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
return key;
|
|
754
|
+
}
|
|
755
|
+
/** Pre-collects @push/@prepend so @stack works (layout typically has @stack before @push in doc order). */
|
|
756
|
+
collectBladeStacks(content, param) {
|
|
757
|
+
if (!param.__bladeStacks)
|
|
758
|
+
param.__bladeStacks = {};
|
|
759
|
+
const expandIncludes = (str, depth = 0) => {
|
|
760
|
+
if (depth > 20)
|
|
761
|
+
return str;
|
|
762
|
+
const incMatch = str.match(/@include\((?<path>['"][^'"]+['"])\)/);
|
|
763
|
+
if (!incMatch)
|
|
764
|
+
return str;
|
|
765
|
+
const p = this.removeQuotes(incMatch.groups?.path || "").trim();
|
|
766
|
+
const included = this.readFile(`views/${p}`);
|
|
767
|
+
const { beforMatch, afterMatch } = this.content(str, incMatch);
|
|
768
|
+
return expandIncludes(beforMatch + included + afterMatch, depth + 1);
|
|
769
|
+
};
|
|
770
|
+
let expanded = expandIncludes(content);
|
|
771
|
+
const pushRe = /@push\s*\((?<name>['"][^'"]+['"])\)(?<body>[\s\S]*?)@endpush/g;
|
|
772
|
+
const prependRe = /@prepend\s*\((?<name>['"][^'"]+['"])\)(?<body>[\s\S]*?)@endprepend/g;
|
|
773
|
+
expanded = expanded.replace(pushRe, (match) => {
|
|
774
|
+
const m = match.match(/@push\s*\((?<name>['"][^'"]+['"])\)(?<body>[\s\S]*?)@endpush/);
|
|
775
|
+
if (m?.groups) {
|
|
776
|
+
const name = this.removeQuotes(m.groups.name).trim();
|
|
777
|
+
param.__bladeStacks[name] =
|
|
778
|
+
(param.__bladeStacks[name] || "") + this.parser(m.groups.body, param);
|
|
779
|
+
}
|
|
780
|
+
return "";
|
|
781
|
+
});
|
|
782
|
+
expanded = expanded.replace(prependRe, (match) => {
|
|
783
|
+
const m = match.match(/@prepend\s*\((?<name>['"][^'"]+['"])\)(?<body>[\s\S]*?)@endprepend/);
|
|
784
|
+
if (m?.groups) {
|
|
785
|
+
const name = this.removeQuotes(m.groups.name).trim();
|
|
786
|
+
param.__bladeStacks[name] =
|
|
787
|
+
this.parser(m.groups.body, param) + (param.__bladeStacks[name] || "");
|
|
788
|
+
}
|
|
789
|
+
return "";
|
|
790
|
+
});
|
|
791
|
+
return expanded;
|
|
251
792
|
}
|
|
252
793
|
async render(filePath, locals, callback) {
|
|
253
794
|
try {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
const
|
|
795
|
+
locals.url = request().url;
|
|
796
|
+
locals.fullUrl = request().fullUrl();
|
|
797
|
+
const resolvedPath = path_1.default.isAbsolute(filePath)
|
|
798
|
+
? filePath
|
|
799
|
+
: path_1.default.join(app_root_path_1.default.path, filePath);
|
|
800
|
+
const content = fs_1.default.readFileSync(resolvedPath, "utf-8");
|
|
801
|
+
this.option = { __bladeStacks: {}, ...locals };
|
|
802
|
+
const cacheKey = this.getExtendsCacheKey(content, resolvedPath);
|
|
803
|
+
const renderedContent = await this.compiler(content, cacheKey);
|
|
257
804
|
return callback(null, renderedContent);
|
|
258
805
|
}
|
|
259
806
|
catch (error) {
|
|
260
|
-
|
|
261
|
-
|
|
807
|
+
const err = error instanceof errors_1.TemplateError
|
|
808
|
+
? error
|
|
809
|
+
: new errors_1.TemplateError(error?.message ?? "Unknown error", undefined, undefined, filePath);
|
|
810
|
+
console.log(colors_1.default.red(err.toString()));
|
|
811
|
+
return callback(error, null);
|
|
262
812
|
}
|
|
263
813
|
}
|
|
264
814
|
Content(path, option) {
|
|
265
815
|
return this.parser(`${this.readFile(path)}`, option);
|
|
266
816
|
}
|
|
817
|
+
/** Compile template string with data (public for compileString, benchmarks). */
|
|
818
|
+
compileTemplate(template, data = {}) {
|
|
819
|
+
const param = { __bladeStacks: {}, ...data };
|
|
820
|
+
return this.parser(template, param);
|
|
821
|
+
}
|
|
822
|
+
/** No-op for API compatibility (call after registerDirective/clearCustomDirectives). */
|
|
823
|
+
clearCombinedRegex() { }
|
|
267
824
|
}
|
|
825
|
+
/** Prefix -> directive key for regex lookup (avoids duplicate named groups in combined regex). */
|
|
826
|
+
TemplatingEngine.PREFIX_TO_KEY = [
|
|
827
|
+
["@includeUnless", "includeUnless"],
|
|
828
|
+
["@includeWhen", "includeWhen"],
|
|
829
|
+
["@includeIf", "includeIf"],
|
|
830
|
+
["@include", "include"],
|
|
831
|
+
["@push", "push"],
|
|
832
|
+
["@prepend", "prepend"],
|
|
833
|
+
["@stack", "stack"],
|
|
834
|
+
["@production", "production"],
|
|
835
|
+
["@env", "env"],
|
|
836
|
+
["@forelse", "forelse"],
|
|
837
|
+
["@foreach", "foreach"],
|
|
838
|
+
["@for", "for"],
|
|
839
|
+
["@while", "while"],
|
|
840
|
+
["@switch", "switch"],
|
|
841
|
+
["@unless", "unless"],
|
|
842
|
+
["@isset", "isset"],
|
|
843
|
+
["@empty", "empty"],
|
|
844
|
+
["@auth", "auth"],
|
|
845
|
+
["@guest", "guest"],
|
|
846
|
+
["@if", "if"],
|
|
847
|
+
["@ternary", "ternary"],
|
|
848
|
+
["{{--", "bladeComment"],
|
|
849
|
+
["{!!", "rawVariable"],
|
|
850
|
+
["{{", "variable"],
|
|
851
|
+
["@json", "json"],
|
|
852
|
+
["@script(", "scriptPath"],
|
|
853
|
+
["@script", "script"],
|
|
854
|
+
["@js", "js"],
|
|
855
|
+
["@inertia", "inertia"],
|
|
856
|
+
["@viteReact", "viteReactRefresh"],
|
|
857
|
+
["@vite([", "vite"],
|
|
858
|
+
["@verbatim", "verbatim"],
|
|
859
|
+
["@method", "methodField"],
|
|
860
|
+
["@csrf", "csrf"],
|
|
861
|
+
];
|
|
862
|
+
TemplatingEngine.MAX_PARSE_DEPTH = 500;
|
|
268
863
|
const engine = new TemplatingEngine();
|
|
864
|
+
/** Clear the extends cache and path cache (e.g. after file changes or plugin changes in dev). */
|
|
865
|
+
function clearCache() {
|
|
866
|
+
extendsCache.clear();
|
|
867
|
+
engine.clearCombinedRegex();
|
|
868
|
+
engine.clearPathCache();
|
|
869
|
+
}
|
|
269
870
|
const compile = engine.Content.bind(engine);
|
|
270
871
|
exports.compile = compile;
|
|
872
|
+
/** Compile a template string with data (for benchmarking, tests). */
|
|
873
|
+
function compileString(template, data = {}) {
|
|
874
|
+
return engine.compileTemplate(template, data);
|
|
875
|
+
}
|
|
271
876
|
const jsBladeEngine = engine;
|
|
272
877
|
exports.jsBladeEngine = jsBladeEngine;
|