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.
Files changed (296) hide show
  1. package/Core/index.d.ts +4 -0
  2. package/Core/index.d.ts.map +1 -1
  3. package/Core/index.js +5 -0
  4. package/__tests__/Job.test.d.ts +2 -0
  5. package/__tests__/Job.test.d.ts.map +1 -0
  6. package/__tests__/Job.test.js +205 -0
  7. package/__tests__/MySqlSchemaBlueprint.test.d.ts +2 -0
  8. package/__tests__/MySqlSchemaBlueprint.test.d.ts.map +1 -0
  9. package/__tests__/MySqlSchemaBlueprint.test.js +55 -0
  10. package/__tests__/PostgresTranslator.test.d.ts +2 -0
  11. package/__tests__/PostgresTranslator.test.d.ts.map +1 -0
  12. package/__tests__/PostgresTranslator.test.js +82 -0
  13. package/__tests__/SQLiteTranslator.test.d.ts +2 -0
  14. package/__tests__/SQLiteTranslator.test.d.ts.map +1 -0
  15. package/__tests__/SQLiteTranslator.test.js +125 -0
  16. package/__tests__/SchemaAlterBlueprint.test.d.ts +2 -0
  17. package/__tests__/SchemaAlterBlueprint.test.d.ts.map +1 -0
  18. package/__tests__/SchemaAlterBlueprint.test.js +35 -0
  19. package/global.d.ts +3 -1
  20. package/index.d.ts +28 -3
  21. package/index.d.ts.map +1 -1
  22. package/index.js +33 -75
  23. package/lib/Application/Application.d.ts.map +1 -1
  24. package/lib/Application/Application.js +2 -2
  25. package/lib/Application/ApplicationBuilder.d.ts.map +1 -1
  26. package/lib/Application/ApplicationBuilder.js +22 -11
  27. package/lib/Auth/AuthMiddleware.d.ts.map +1 -1
  28. package/lib/Auth/AuthMiddleware.js +29 -12
  29. package/lib/Auth/index.d.ts +17 -3
  30. package/lib/Auth/index.d.ts.map +1 -1
  31. package/lib/Auth/index.js +128 -26
  32. package/lib/Auth/loginRateLimit.d.ts +6 -0
  33. package/lib/Auth/loginRateLimit.d.ts.map +1 -0
  34. package/lib/Auth/loginRateLimit.js +25 -0
  35. package/lib/Auth/refreshTokenStore.d.ts +24 -0
  36. package/lib/Auth/refreshTokenStore.d.ts.map +1 -0
  37. package/lib/Auth/refreshTokenStore.js +46 -0
  38. package/lib/Command-Line/DBCommand.d.ts.map +1 -1
  39. package/lib/Command-Line/DBCommand.js +9 -3
  40. package/lib/Command-Line/KeyGenerateCommand.d.ts +6 -0
  41. package/lib/Command-Line/KeyGenerateCommand.d.ts.map +1 -0
  42. package/lib/Command-Line/KeyGenerateCommand.js +47 -0
  43. package/lib/Command-Line/MakeCommand.d.ts +1 -0
  44. package/lib/Command-Line/MakeCommand.d.ts.map +1 -1
  45. package/lib/Command-Line/MakeCommand.js +36 -32
  46. package/lib/Command-Line/NodeArtisanCommand.d.ts +2 -0
  47. package/lib/Command-Line/NodeArtisanCommand.d.ts.map +1 -1
  48. package/lib/Command-Line/NodeArtisanCommand.js +24 -6
  49. package/lib/Command-Line/NodeTinker/Tinker.d.ts +5 -2
  50. package/lib/Command-Line/NodeTinker/Tinker.d.ts.map +1 -1
  51. package/lib/Command-Line/NodeTinker/Tinker.js +13 -3
  52. package/lib/Command-Line/WatchCommand.d.ts +29 -0
  53. package/lib/Command-Line/WatchCommand.d.ts.map +1 -0
  54. package/lib/Command-Line/WatchCommand.js +234 -0
  55. package/lib/Command-Line/files/Models.d.ts.map +1 -1
  56. package/lib/Command-Line/files/Models.js +67 -0
  57. package/lib/Console/Command.d.ts +18 -0
  58. package/lib/Console/Command.d.ts.map +1 -1
  59. package/lib/Console/Command.js +29 -0
  60. package/lib/Container/index.d.ts.map +1 -1
  61. package/lib/Container/index.js +22 -8
  62. package/lib/Database/Database.d.ts +21 -0
  63. package/lib/Database/Database.d.ts.map +1 -0
  64. package/lib/Database/Database.js +55 -0
  65. package/lib/Database/DatabaseServiceProvider.d.ts +22 -0
  66. package/lib/Database/DatabaseServiceProvider.d.ts.map +1 -0
  67. package/lib/Database/DatabaseServiceProvider.js +50 -0
  68. package/lib/Database/Drivers/KnexDriver.d.ts +13 -0
  69. package/lib/Database/Drivers/KnexDriver.d.ts.map +1 -0
  70. package/lib/Database/Drivers/KnexDriver.js +29 -0
  71. package/lib/Database/Drivers/MongooseDriver.d.ts +20 -0
  72. package/lib/Database/Drivers/MongooseDriver.d.ts.map +1 -0
  73. package/lib/Database/Drivers/MongooseDriver.js +57 -0
  74. package/lib/Database/Drivers/SequelizeDriver.d.ts +24 -0
  75. package/lib/Database/Drivers/SequelizeDriver.d.ts.map +1 -0
  76. package/lib/Database/Drivers/SequelizeDriver.js +82 -0
  77. package/lib/Database/index.d.ts +7 -0
  78. package/lib/Database/index.d.ts.map +1 -0
  79. package/lib/Database/index.js +11 -0
  80. package/lib/Database/interface.d.ts +14 -0
  81. package/lib/Database/interface.d.ts.map +1 -0
  82. package/lib/Database/interface.js +2 -0
  83. package/lib/Database/type.d.ts +18 -0
  84. package/lib/Database/type.d.ts.map +1 -0
  85. package/lib/Database/type.js +2 -0
  86. package/lib/Error/DisplayErrorCode.d.ts.map +1 -1
  87. package/lib/Error/DisplayErrorCode.js +27 -17
  88. package/lib/Error/MissMatchTokenException/index.d.ts +4 -0
  89. package/lib/Error/MissMatchTokenException/index.d.ts.map +1 -0
  90. package/lib/Error/MissMatchTokenException/index.js +9 -0
  91. package/lib/Error/SocialiteAuthError/SocialiteAuthError.d.ts +9 -0
  92. package/lib/Error/SocialiteAuthError/SocialiteAuthError.d.ts.map +1 -0
  93. package/lib/Error/SocialiteAuthError/SocialiteAuthError.js +17 -0
  94. package/lib/Global/helpers.d.ts.map +1 -1
  95. package/lib/Global/helpers.js +7 -6
  96. package/lib/Http/index.d.ts +3 -1
  97. package/lib/Http/index.d.ts.map +1 -1
  98. package/lib/Http/index.js +26 -5
  99. package/lib/Interface/index.d.ts +17 -3
  100. package/lib/Interface/index.d.ts.map +1 -1
  101. package/lib/Jcc-eloquent/lib/Builder.d.ts +12 -1
  102. package/lib/Jcc-eloquent/lib/Builder.d.ts.map +1 -1
  103. package/lib/Jcc-eloquent/lib/Builder.js +119 -5
  104. package/lib/Jcc-eloquent/lib/Database/index.d.ts.map +1 -1
  105. package/lib/Jcc-eloquent/lib/Database/index.js +2 -1
  106. package/lib/Jcc-eloquent/lib/Interfaces/index.d.ts +37 -2
  107. package/lib/Jcc-eloquent/lib/Interfaces/index.d.ts.map +1 -1
  108. package/lib/Jcc-eloquent/lib/Migration.d.ts +6 -0
  109. package/lib/Jcc-eloquent/lib/Migration.d.ts.map +1 -1
  110. package/lib/Jcc-eloquent/lib/Migration.js +24 -2
  111. package/lib/Jcc-eloquent/lib/Model.d.ts +24 -7
  112. package/lib/Jcc-eloquent/lib/Model.d.ts.map +1 -1
  113. package/lib/Jcc-eloquent/lib/Model.js +35 -2
  114. package/lib/Jcc-eloquent/lib/QueryBuilder.d.ts +2 -1
  115. package/lib/Jcc-eloquent/lib/QueryBuilder.d.ts.map +1 -1
  116. package/lib/Jcc-eloquent/lib/QueryBuilder.js +10 -1
  117. package/lib/Jcc-eloquent/lib/Rollback.d.ts.map +1 -1
  118. package/lib/Jcc-eloquent/lib/Rollback.js +9 -2
  119. package/lib/Jcc-eloquent/lib/Schema/BaseSchemaEntity/index.d.ts.map +1 -1
  120. package/lib/Jcc-eloquent/lib/Schema/BaseSchemaEntity/index.js +3 -3
  121. package/lib/Jcc-eloquent/lib/Schema/BluePrint/index.d.ts +6 -2
  122. package/lib/Jcc-eloquent/lib/Schema/BluePrint/index.d.ts.map +1 -1
  123. package/lib/Jcc-eloquent/lib/Schema/BluePrint/index.js +41 -12
  124. package/lib/Jcc-eloquent/lib/Schema/Translator/Sqlite.d.ts.map +1 -1
  125. package/lib/Jcc-eloquent/lib/Schema/Translator/Sqlite.js +21 -3
  126. package/lib/Jcc-eloquent/lib/Schema/Translator/postgres.d.ts +1 -0
  127. package/lib/Jcc-eloquent/lib/Schema/Translator/postgres.d.ts.map +1 -1
  128. package/lib/Jcc-eloquent/lib/Schema/Translator/postgres.js +70 -14
  129. package/lib/Jcc-eloquent/lib/Schema/index.d.ts +12 -0
  130. package/lib/Jcc-eloquent/lib/Schema/index.d.ts.map +1 -1
  131. package/lib/Jcc-eloquent/lib/Schema/index.js +27 -6
  132. package/lib/Jcc-eloquent/lib/utils/index.d.ts +13 -0
  133. package/lib/Jcc-eloquent/lib/utils/index.d.ts.map +1 -1
  134. package/lib/Jcc-eloquent/lib/utils/index.js +62 -0
  135. package/lib/Middleware/index.d.ts +1 -0
  136. package/lib/Middleware/index.d.ts.map +1 -1
  137. package/lib/Middleware/index.js +20 -8
  138. package/lib/Model/Sequelize.d.ts +3 -0
  139. package/lib/Model/Sequelize.d.ts.map +1 -0
  140. package/lib/Model/Sequelize.js +6 -0
  141. package/lib/Providers/RouteServiceProvider.d.ts.map +1 -1
  142. package/lib/Providers/RouteServiceProvider.js +3 -0
  143. package/lib/Providers/SessionServiceProvider.d.ts +12 -0
  144. package/lib/Providers/SessionServiceProvider.d.ts.map +1 -0
  145. package/lib/Providers/SessionServiceProvider.js +47 -0
  146. package/lib/Queue/Controllers/QueueControllers.d.ts +31 -0
  147. package/lib/Queue/Controllers/QueueControllers.d.ts.map +1 -0
  148. package/lib/Queue/Controllers/QueueControllers.js +126 -0
  149. package/lib/Queue/Drivers/DatabaseDriver.d.ts +6 -1
  150. package/lib/Queue/Drivers/DatabaseDriver.d.ts.map +1 -1
  151. package/lib/Queue/Drivers/DatabaseDriver.js +76 -21
  152. package/lib/Queue/Drivers/MemoryDriver.d.ts +17 -6
  153. package/lib/Queue/Drivers/MemoryDriver.d.ts.map +1 -1
  154. package/lib/Queue/Drivers/MemoryDriver.js +323 -14
  155. package/lib/Queue/Drivers/RedisDriver.d.ts +48 -0
  156. package/lib/Queue/Drivers/RedisDriver.d.ts.map +1 -0
  157. package/lib/Queue/Drivers/RedisDriver.js +533 -0
  158. package/lib/Queue/Job.d.ts +8 -0
  159. package/lib/Queue/Job.d.ts.map +1 -1
  160. package/lib/Queue/Job.js +25 -1
  161. package/lib/Queue/JobsLogger.d.ts.map +1 -1
  162. package/lib/Queue/JobsLogger.js +6 -3
  163. package/lib/Queue/Queue.d.ts +17 -0
  164. package/lib/Queue/Queue.d.ts.map +1 -1
  165. package/lib/Queue/Queue.js +146 -11
  166. package/lib/Queue/QueueServiceProvider.d.ts +6 -0
  167. package/lib/Queue/QueueServiceProvider.d.ts.map +1 -0
  168. package/lib/Queue/QueueServiceProvider.js +18 -0
  169. package/lib/Queue/Route/web.d.ts +2 -0
  170. package/lib/Queue/Route/web.d.ts.map +1 -0
  171. package/lib/Queue/Route/web.js +16 -0
  172. package/lib/Queue/index.d.ts +1 -0
  173. package/lib/Queue/index.d.ts.map +1 -1
  174. package/lib/Queue/index.js +3 -0
  175. package/lib/Queue/interface.d.ts +25 -0
  176. package/lib/Queue/interface.d.ts.map +1 -1
  177. package/lib/Queue/middleware/queueDashboardGuard.d.ts +7 -0
  178. package/lib/Queue/middleware/queueDashboardGuard.d.ts.map +1 -0
  179. package/lib/Queue/middleware/queueDashboardGuard.js +43 -0
  180. package/lib/Queue/type.d.ts +12 -1
  181. package/lib/Queue/type.d.ts.map +1 -1
  182. package/lib/Response/index.d.ts.map +1 -1
  183. package/lib/Response/index.js +6 -1
  184. package/lib/Routes/Route.d.ts +2 -2
  185. package/lib/Routes/Route.d.ts.map +1 -1
  186. package/lib/Routes/RouteBuilder.d.ts +2 -2
  187. package/lib/Routes/RouteBuilder.d.ts.map +1 -1
  188. package/lib/Security/CsrfMiddleware.d.ts +37 -0
  189. package/lib/Security/CsrfMiddleware.d.ts.map +1 -0
  190. package/lib/Security/CsrfMiddleware.js +85 -0
  191. package/lib/Security/MethodSpoofingMiddleware.d.ts +36 -0
  192. package/lib/Security/MethodSpoofingMiddleware.d.ts.map +1 -0
  193. package/lib/Security/MethodSpoofingMiddleware.js +51 -0
  194. package/lib/Security/index.d.ts +5 -0
  195. package/lib/Security/index.d.ts.map +1 -0
  196. package/lib/Security/index.js +7 -0
  197. package/lib/Session/DatabaseSession.d.ts +9 -3
  198. package/lib/Session/DatabaseSession.d.ts.map +1 -1
  199. package/lib/Session/DatabaseSession.js +38 -36
  200. package/lib/Session/ExpressJccSession.d.ts +23 -0
  201. package/lib/Session/ExpressJccSession.d.ts.map +1 -0
  202. package/lib/Session/ExpressJccSession.js +110 -0
  203. package/lib/Session/RedisSession.d.ts +2 -2
  204. package/lib/Session/RedisSession.d.ts.map +1 -1
  205. package/lib/Session/RedisSession.js +19 -5
  206. package/lib/Session/SessionManager.d.ts +18 -4
  207. package/lib/Session/SessionManager.d.ts.map +1 -1
  208. package/lib/Session/SessionManager.js +53 -17
  209. package/lib/Socialite/AbstractProvider.d.ts +60 -0
  210. package/lib/Socialite/AbstractProvider.d.ts.map +1 -0
  211. package/lib/Socialite/AbstractProvider.js +136 -0
  212. package/lib/Socialite/Drivers/facebook/FacebookDriver.d.ts +9 -0
  213. package/lib/Socialite/Drivers/facebook/FacebookDriver.d.ts.map +1 -0
  214. package/lib/Socialite/Drivers/facebook/FacebookDriver.js +40 -0
  215. package/lib/Socialite/Drivers/facebook/FacebookProvider.d.ts +15 -0
  216. package/lib/Socialite/Drivers/facebook/FacebookProvider.d.ts.map +1 -0
  217. package/lib/Socialite/Drivers/facebook/FacebookProvider.js +28 -0
  218. package/lib/Socialite/Drivers/github/GitHubDriver.d.ts +9 -0
  219. package/lib/Socialite/Drivers/github/GitHubDriver.d.ts.map +1 -0
  220. package/lib/Socialite/Drivers/github/GitHubDriver.js +54 -0
  221. package/lib/Socialite/Drivers/github/GitHubProvider.d.ts +15 -0
  222. package/lib/Socialite/Drivers/github/GitHubProvider.d.ts.map +1 -0
  223. package/lib/Socialite/Drivers/github/GitHubProvider.js +28 -0
  224. package/lib/Socialite/Drivers/gitlab/GitLabDriver.d.ts +10 -0
  225. package/lib/Socialite/Drivers/gitlab/GitLabDriver.d.ts.map +1 -0
  226. package/lib/Socialite/Drivers/gitlab/GitLabDriver.js +37 -0
  227. package/lib/Socialite/Drivers/gitlab/GitLabProvider.d.ts +15 -0
  228. package/lib/Socialite/Drivers/gitlab/GitLabProvider.d.ts.map +1 -0
  229. package/lib/Socialite/Drivers/gitlab/GitLabProvider.js +28 -0
  230. package/lib/Socialite/Drivers/google/GoogleDriver.d.ts +9 -0
  231. package/lib/Socialite/Drivers/google/GoogleDriver.d.ts.map +1 -0
  232. package/lib/Socialite/Drivers/google/GoogleDriver.js +39 -0
  233. package/lib/Socialite/Drivers/google/GoogleProvider.d.ts +15 -0
  234. package/lib/Socialite/Drivers/google/GoogleProvider.d.ts.map +1 -0
  235. package/lib/Socialite/Drivers/google/GoogleProvider.js +28 -0
  236. package/lib/Socialite/Drivers/slack/SlackDriver.d.ts +9 -0
  237. package/lib/Socialite/Drivers/slack/SlackDriver.d.ts.map +1 -0
  238. package/lib/Socialite/Drivers/slack/SlackDriver.js +41 -0
  239. package/lib/Socialite/Drivers/slack/SlackProvider.d.ts +16 -0
  240. package/lib/Socialite/Drivers/slack/SlackProvider.d.ts.map +1 -0
  241. package/lib/Socialite/Drivers/slack/SlackProvider.js +37 -0
  242. package/lib/Socialite/Drivers/twitter/TwitterDriver.d.ts +9 -0
  243. package/lib/Socialite/Drivers/twitter/TwitterDriver.d.ts.map +1 -0
  244. package/lib/Socialite/Drivers/twitter/TwitterDriver.js +57 -0
  245. package/lib/Socialite/Drivers/twitter/TwitterProvider.d.ts +16 -0
  246. package/lib/Socialite/Drivers/twitter/TwitterProvider.d.ts.map +1 -0
  247. package/lib/Socialite/Drivers/twitter/TwitterProvider.js +38 -0
  248. package/lib/Socialite/SocialUser.d.ts +42 -0
  249. package/lib/Socialite/SocialUser.d.ts.map +1 -0
  250. package/lib/Socialite/SocialUser.js +116 -0
  251. package/lib/Socialite/Socialite.d.ts +13 -0
  252. package/lib/Socialite/Socialite.d.ts.map +1 -0
  253. package/lib/Socialite/Socialite.js +41 -0
  254. package/lib/Socialite/SocialiteProvider.d.ts +9 -0
  255. package/lib/Socialite/SocialiteProvider.d.ts.map +1 -0
  256. package/lib/Socialite/SocialiteProvider.js +15 -0
  257. package/lib/Socialite/config.d.ts +7 -0
  258. package/lib/Socialite/config.d.ts.map +1 -0
  259. package/lib/Socialite/config.js +44 -0
  260. package/lib/Socialite/constant.d.ts +18 -0
  261. package/lib/Socialite/constant.d.ts.map +1 -0
  262. package/lib/Socialite/constant.js +20 -0
  263. package/lib/Socialite/index.d.ts +13 -0
  264. package/lib/Socialite/index.d.ts.map +1 -0
  265. package/lib/Socialite/index.js +25 -0
  266. package/lib/Socialite/types.d.ts +13 -0
  267. package/lib/Socialite/types.d.ts.map +1 -0
  268. package/lib/Socialite/types.js +2 -0
  269. package/lib/Templating-engine/benchmark.d.ts +7 -0
  270. package/lib/Templating-engine/benchmark.d.ts.map +1 -0
  271. package/lib/Templating-engine/benchmark.js +44 -0
  272. package/lib/Templating-engine/engineHelper.d.ts +37 -8
  273. package/lib/Templating-engine/engineHelper.d.ts.map +1 -1
  274. package/lib/Templating-engine/engineHelper.js +203 -26
  275. package/lib/Templating-engine/errors.d.ts +15 -0
  276. package/lib/Templating-engine/errors.d.ts.map +1 -0
  277. package/lib/Templating-engine/errors.js +35 -0
  278. package/lib/Templating-engine/expressions.d.ts +24 -0
  279. package/lib/Templating-engine/expressions.d.ts.map +1 -1
  280. package/lib/Templating-engine/expressions.js +27 -5
  281. package/lib/Templating-engine/index.d.ts +87 -17
  282. package/lib/Templating-engine/index.d.ts.map +1 -1
  283. package/lib/Templating-engine/index.js +701 -96
  284. package/lib/Templating-engine/plugins.d.ts +30 -0
  285. package/lib/Templating-engine/plugins.d.ts.map +1 -0
  286. package/lib/Templating-engine/plugins.js +47 -0
  287. package/lib/Type/index.d.ts +1 -0
  288. package/lib/Type/index.d.ts.map +1 -1
  289. package/lib/Validation/Validator/CustomValidation.d.ts.map +1 -1
  290. package/lib/Validation/Validator/CustomValidation.js +2 -2
  291. package/lib/Validation/Validator/helper.d.ts.map +1 -1
  292. package/lib/Validation/Validator/helper.js +8 -1
  293. package/lib/util/index.d.ts +42 -1
  294. package/lib/util/index.d.ts.map +1 -1
  295. package/lib/util/index.js +154 -17
  296. 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 variable directives in the template content.
18
- * @param {string} content - Template content.
19
- * @param {Object} match - Match object from the regular expression.
20
- * @param {Object} param - Local variables for rendering.
21
- * @returns {string} - Rendered content.
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.param || "";
25
- if (variableName.trim().startsWith("assets")) {
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.replace(/{{\s*([^}]+)\s*}}/, assets);
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.accessNestedProperty(param, variableName);
31
- const newContent = content.replace(/{{\s*([^}]+)\s*}}/, variableValue);
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 in the template content.
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.loopData);
44
- if (Array.isArray(loopData)) {
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
- result = loopData
47
- .map((item, index) => {
48
- const loopContext = {
49
- ...param,
50
- [match.groups.loopvariable]: item,
51
- };
52
- loopContext[match.groups.loopvariable]["loopIndex"] = index;
53
- return this.parser(loopBody + "@endforeach", loopContext);
54
- })
55
- .join("");
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
- const finalResult = beforMatch + result + afterListResult;
60
- return finalResult.replace(/@endforeach/gm, "");
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 ifData = match?.groups.ifData;
72
- const value = this.evaluateCondition(ifData, param);
73
- if (value) {
74
- result = this.parser(match.groups?.ifBody, param);
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
- else {
77
- if (match?.groups?.elseBody) {
78
- result = this.parser(match.groups?.elseBody, param);
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
- let result = "";
93
- const value = this.evaluateCondition(content, param);
94
- if (value) {
95
- result = this.parser(match.groups?.trueData, param);
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, param);
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
- if (Object.values(param?.Auth || {}).length > 1) {
148
- const authData = match?.groups?.authData?.replace(/(\'|\")/g, "");
149
- if ((authData && param?.Auth?.role === authData) ||
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.injectReactRefefresh();
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
- * @param {string} content - Template content.
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
- const match = content.match(new RegExp(Object.values(this.directives)
211
- .map((regex) => `(${regex.source})`)
212
- .join("|")));
213
- if (!match)
214
- return content;
215
- switch (true) {
216
- case match[0].startsWith("@include"):
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
- * @param {string} content - Template content.
243
- * @returns {Promise<string>} - Compiled content.
720
+ * Caches extends result in production for performance.
244
721
  */
245
- async compiler(content) {
246
- const newContent = await this.extends(content);
247
- const finalContent = this.parser(newContent);
248
- return finalContent
249
- .replace(/(\{\{\-\-)/gm, "<!--")
250
- .replace(/(\-\-\}\})/gm, "-->");
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
- const content = fs_1.default.readFileSync(filePath, "utf-8");
255
- this.option = locals;
256
- const renderedContent = await this.compiler(content);
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
- return console.log(colors_1.default.red(error.message));
261
- // throw new AppError(error?.message, DEFAULT_ENGINE_ERROR);
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;