odac 0.9.0 → 1.0.0
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/.github/workflows/auto-pr-description.yml +0 -2
- package/.github/workflows/codeql.yml +46 -0
- package/.github/workflows/release.yml +13 -6
- package/.github/workflows/test-coverage.yml +10 -9
- package/.releaserc.js +9 -6
- package/CHANGELOG.md +62 -150
- package/CODE_OF_CONDUCT.md +1 -1
- package/CONTRIBUTING.md +8 -8
- package/LICENSE +21 -661
- package/README.md +12 -12
- package/SECURITY.md +4 -4
- package/bin/odac.js +101 -0
- package/{framework/web/candy.js → client/odac.js} +310 -44
- package/docs/backend/01-overview/{01-whats-in-the-candy-box.md → 01-whats-in-the-odac-box.md} +4 -2
- package/docs/backend/01-overview/02-super-handy-helper-functions.md +29 -1
- package/docs/backend/01-overview/03-development-server.md +11 -11
- package/docs/backend/02-structure/01-typical-project-layout.md +4 -4
- package/docs/backend/03-config/00-configuration-overview.md +6 -6
- package/docs/backend/03-config/01-database-connection.md +1 -1
- package/docs/backend/03-config/02-static-route-mapping-optional.md +4 -4
- package/docs/backend/03-config/04-environment-variables.md +20 -20
- package/docs/backend/03-config/05-early-hints.md +4 -4
- package/docs/backend/04-routing/01-basic-page-routes.md +4 -4
- package/docs/backend/04-routing/02-controller-less-view-routes.md +5 -5
- package/docs/backend/04-routing/03-api-and-data-routes.md +3 -3
- package/docs/backend/04-routing/04-authentication-aware-routes.md +5 -5
- package/docs/backend/04-routing/05-advanced-routing.md +3 -3
- package/docs/backend/04-routing/06-error-pages.md +17 -17
- package/docs/backend/04-routing/07-cron-jobs.md +13 -13
- package/docs/backend/04-routing/08-middleware.md +214 -0
- package/docs/backend/04-routing/09-websocket-auth-middleware.md +292 -0
- package/docs/backend/04-routing/09-websocket-examples.md +381 -0
- package/docs/backend/04-routing/09-websocket-quick-reference.md +211 -0
- package/docs/backend/04-routing/09-websocket.md +298 -0
- package/docs/backend/05-controllers/01-how-to-build-a-controller.md +3 -3
- package/docs/backend/05-controllers/02-your-trusty-odac-assistant.md +41 -0
- package/docs/backend/05-controllers/03-controller-classes.md +19 -19
- package/docs/backend/05-forms/01-custom-forms.md +114 -114
- package/docs/backend/05-forms/02-automatic-database-insert.md +82 -82
- package/docs/backend/06-request-and-response/01-the-request-object-what-is-the-user-asking-for.md +26 -26
- package/docs/backend/06-request-and-response/02-sending-a-response-replying-to-the-user.md +10 -10
- package/docs/backend/07-views/01-the-view-directory.md +1 -1
- package/docs/backend/07-views/02-rendering-a-view.md +22 -22
- package/docs/backend/07-views/03-template-syntax.md +52 -52
- package/docs/backend/07-views/03-variables.md +84 -84
- package/docs/backend/07-views/04-request-data.md +57 -57
- package/docs/backend/07-views/05-conditionals.md +78 -78
- package/docs/backend/07-views/06-loops.md +114 -114
- package/docs/backend/07-views/07-translations.md +66 -66
- package/docs/backend/07-views/08-backend-javascript.md +103 -103
- package/docs/backend/07-views/09-comments.md +71 -71
- package/docs/backend/08-database/01-database-connection.md +8 -8
- package/docs/backend/08-database/02-using-mysql.md +49 -49
- package/docs/backend/09-validation/01-the-validator-service.md +38 -38
- package/docs/backend/10-authentication/01-user-logins-with-authjs.md +15 -15
- package/docs/backend/10-authentication/02-foiling-villains-with-csrf-protection.md +10 -10
- package/docs/backend/10-authentication/03-register.md +12 -12
- package/docs/backend/10-authentication/{04-candy-register-forms.md → 04-odac-register-forms.md} +141 -141
- package/docs/backend/10-authentication/05-session-management.md +10 -10
- package/docs/backend/10-authentication/{06-candy-login-forms.md → 06-odac-login-forms.md} +125 -125
- package/docs/backend/11-mail/01-the-mail-service.md +5 -5
- package/docs/backend/12-streaming/01-streaming-overview.md +96 -54
- package/docs/backend/13-utilities/{01-candy-var.md → 01-odac-var.md} +109 -109
- package/docs/frontend/01-overview/01-introduction.md +30 -30
- package/docs/frontend/02-ajax-navigation/01-quick-start.md +45 -45
- package/docs/frontend/02-ajax-navigation/02-configuration.md +14 -14
- package/docs/frontend/02-ajax-navigation/03-advanced-usage.md +36 -36
- package/docs/frontend/03-forms/01-form-handling.md +32 -32
- package/docs/frontend/04-api-requests/01-get-post.md +33 -33
- package/docs/frontend/05-streaming/01-client-streaming.md +15 -15
- package/docs/frontend/06-websocket/00-overview.md +76 -0
- package/docs/frontend/06-websocket/01-websocket-client.md +139 -0
- package/docs/frontend/06-websocket/02-shared-websocket.md +149 -0
- package/docs/index.json +49 -11
- package/eslint.config.mjs +6 -6
- package/{framework/index.js → index.js} +1 -1
- package/package.json +14 -39
- package/{framework/src → src}/Auth.js +59 -59
- package/{framework/src → src}/Config.js +3 -3
- package/{framework/src → src}/Lang.js +7 -7
- package/{framework/src → src}/Mail.js +5 -5
- package/{framework/src → src}/Mysql.js +42 -42
- package/src/Odac.js +112 -0
- package/{framework/src → src}/Request.js +38 -36
- package/{framework/src → src}/Route/Internal.js +116 -116
- package/src/Route/Middleware.js +75 -0
- package/src/Route.js +621 -0
- package/src/Server.js +22 -0
- package/{framework/src → src}/Stream.js +11 -3
- package/{framework/src → src}/Validator.js +21 -21
- package/{framework/src → src}/Var.js +5 -5
- package/{framework/src → src}/View/EarlyHints.js +1 -1
- package/{framework/src → src}/View/Form.js +69 -69
- package/{framework/src → src}/View.js +78 -81
- package/src/WebSocket.js +403 -0
- package/template/config.json +5 -0
- package/{web → template}/controller/page/about.js +6 -6
- package/{web → template}/controller/page/index.js +9 -9
- package/{web → template}/package.json +4 -5
- package/{web → template}/public/assets/css/style.css +4 -4
- package/{web → template}/public/assets/js/app.js +6 -6
- package/{web → template}/route/www.js +6 -6
- package/{web → template}/skeleton/main.html +1 -1
- package/{web → template}/view/content/about.html +5 -5
- package/{web → template}/view/content/home.html +12 -12
- package/template/view/footer/main.html +11 -0
- package/{web → template}/view/head/main.html +1 -1
- package/{web → template}/view/header/main.html +2 -2
- package/test/core/Candy.test.js +58 -58
- package/test/core/Commands.test.js +7 -7
- package/test/core/Config.test.js +82 -85
- package/test/core/Lang.test.js +2 -2
- package/test/core/Process.test.js +6 -6
- package/test/framework/Route.test.js +56 -37
- package/test/framework/View/EarlyHints.test.js +2 -2
- package/test/framework/WebSocket.test.js +100 -0
- package/test/framework/middleware.test.js +85 -0
- package/test/server/Api.test.js +31 -31
- package/test/server/DNS.test.js +11 -11
- package/test/server/Hub.test.js +497 -0
- package/test/server/Mail.account.test_.js +3 -3
- package/test/server/Mail.init.test_.js +10 -10
- package/test/server/Mail.test_.js +20 -20
- package/test/server/SSL.test_.js +54 -54
- package/test/server/Server.test.js +39 -39
- package/test/server/Service.test_.js +7 -7
- package/test/server/Subdomain.test.js +7 -7
- package/test/server/Web/Firewall.test.js +87 -87
- package/test/server/Web/Proxy.test.js +397 -0
- package/test/server/{Web.test_.js → Web.test.js} +137 -205
- package/test/server/__mocks__/fs.js +2 -2
- package/test/server/__mocks__/{globalCandy.js → globalOdac.js} +5 -5
- package/test/server/__mocks__/index.js +6 -6
- package/test/server/__mocks__/testFactories.js +1 -1
- package/test/server/__mocks__/testHelpers.js +7 -7
- package/.husky/pre-commit +0 -2
- package/.kiro/steering/code-style.md +0 -56
- package/.kiro/steering/product.md +0 -20
- package/.kiro/steering/structure.md +0 -77
- package/.kiro/steering/tech.md +0 -87
- package/AGENTS.md +0 -84
- package/bin/candy +0 -10
- package/bin/candypack +0 -10
- package/cli/index.js +0 -3
- package/cli/src/Cli.js +0 -348
- package/cli/src/Connector.js +0 -93
- package/cli/src/Monitor.js +0 -416
- package/core/Candy.js +0 -87
- package/core/Commands.js +0 -239
- package/core/Config.js +0 -1094
- package/core/Lang.js +0 -52
- package/core/Log.js +0 -43
- package/core/Process.js +0 -26
- package/docs/backend/05-controllers/02-your-trusty-candy-assistant.md +0 -20
- package/docs/server/01-installation/01-quick-install.md +0 -19
- package/docs/server/01-installation/02-manual-installation-via-npm.md +0 -9
- package/docs/server/02-get-started/01-core-concepts.md +0 -7
- package/docs/server/02-get-started/02-basic-commands.md +0 -57
- package/docs/server/02-get-started/03-cli-reference.md +0 -276
- package/docs/server/02-get-started/04-cli-quick-reference.md +0 -102
- package/docs/server/03-service/01-start-a-new-service.md +0 -57
- package/docs/server/03-service/02-delete-a-service.md +0 -48
- package/docs/server/04-web/01-create-a-website.md +0 -36
- package/docs/server/04-web/02-list-websites.md +0 -9
- package/docs/server/04-web/03-delete-a-website.md +0 -29
- package/docs/server/05-subdomain/01-create-a-subdomain.md +0 -32
- package/docs/server/05-subdomain/02-list-subdomains.md +0 -33
- package/docs/server/05-subdomain/03-delete-a-subdomain.md +0 -41
- package/docs/server/06-ssl/01-renew-an-ssl-certificate.md +0 -34
- package/docs/server/07-mail/01-create-a-mail-account.md +0 -23
- package/docs/server/07-mail/02-delete-a-mail-account.md +0 -20
- package/docs/server/07-mail/03-list-mail-accounts.md +0 -20
- package/docs/server/07-mail/04-change-account-password.md +0 -23
- package/framework/src/Candy.js +0 -81
- package/framework/src/Route.js +0 -455
- package/framework/src/Server.js +0 -15
- package/locale/de-DE.json +0 -80
- package/locale/en-US.json +0 -79
- package/locale/es-ES.json +0 -80
- package/locale/fr-FR.json +0 -80
- package/locale/pt-BR.json +0 -80
- package/locale/ru-RU.json +0 -80
- package/locale/tr-TR.json +0 -85
- package/locale/zh-CN.json +0 -80
- package/server/index.js +0 -5
- package/server/src/Api.js +0 -88
- package/server/src/DNS.js +0 -940
- package/server/src/Hub.js +0 -535
- package/server/src/Mail.js +0 -571
- package/server/src/SSL.js +0 -180
- package/server/src/Server.js +0 -27
- package/server/src/Service.js +0 -248
- package/server/src/Subdomain.js +0 -64
- package/server/src/Web/Firewall.js +0 -170
- package/server/src/Web/Proxy.js +0 -134
- package/server/src/Web.js +0 -451
- package/server/src/mail/imap.js +0 -1091
- package/server/src/mail/server.js +0 -32
- package/server/src/mail/smtp.js +0 -786
- package/test/server/Client.test.js +0 -338
- package/test/server/__mocks__/http-proxy.js +0 -105
- package/watchdog/index.js +0 -3
- package/watchdog/src/Watchdog.js +0 -156
- package/web/config.json +0 -5
- package/web/view/footer/main.html +0 -11
- /package/{framework/src → src}/Env.js +0 -0
- /package/{framework/src → src}/Route/Cron.js +0 -0
- /package/{framework/src → src}/Token.js +0 -0
|
@@ -20,7 +20,7 @@ class View {
|
|
|
20
20
|
end: '*/ html += `'
|
|
21
21
|
},
|
|
22
22
|
'{{': {
|
|
23
|
-
function: '${
|
|
23
|
+
function: '${Odac.Var(await ',
|
|
24
24
|
close: '}}',
|
|
25
25
|
end: ').html().replace(/\\n/g, "<br>")}'
|
|
26
26
|
},
|
|
@@ -30,7 +30,7 @@ class View {
|
|
|
30
30
|
},
|
|
31
31
|
component: {
|
|
32
32
|
// TODO: Implement component
|
|
33
|
-
// <
|
|
33
|
+
// <odac:component name="navbar" title="Dashboard"/>
|
|
34
34
|
},
|
|
35
35
|
continue: {
|
|
36
36
|
function: 'continue;',
|
|
@@ -50,7 +50,7 @@ class View {
|
|
|
50
50
|
},
|
|
51
51
|
fetch: {
|
|
52
52
|
// TODO: Implement fetch
|
|
53
|
-
// <
|
|
53
|
+
// <odac:fetch fetch="/get/products" as="data" method="GET" headers="{}" body="null" refresh="false">
|
|
54
54
|
},
|
|
55
55
|
for: {
|
|
56
56
|
function: '{ let _arr = $constructor; for(let $key in _arr){ let $value = _arr[$key];',
|
|
@@ -68,16 +68,16 @@ class View {
|
|
|
68
68
|
condition: true
|
|
69
69
|
}
|
|
70
70
|
},
|
|
71
|
-
'<
|
|
71
|
+
'<odac:js>': {
|
|
72
72
|
end: ' html += `',
|
|
73
73
|
function: '`; ',
|
|
74
|
-
close: '</
|
|
74
|
+
close: '</odac:js>'
|
|
75
75
|
},
|
|
76
76
|
lazy: {
|
|
77
77
|
// TODO: Implement lazy
|
|
78
|
-
// <
|
|
79
|
-
// <
|
|
80
|
-
// </
|
|
78
|
+
// <odac:lazy>
|
|
79
|
+
// <odac:component name="profile-card" data="user"/>
|
|
80
|
+
// </odac:lazy>
|
|
81
81
|
},
|
|
82
82
|
list: {
|
|
83
83
|
arguments: {
|
|
@@ -98,21 +98,21 @@ class View {
|
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
#part = {}
|
|
101
|
-
#
|
|
101
|
+
#odac = null
|
|
102
102
|
|
|
103
|
-
constructor(
|
|
104
|
-
this.#
|
|
103
|
+
constructor(odac) {
|
|
104
|
+
this.#odac = odac
|
|
105
105
|
|
|
106
|
-
if (!global.
|
|
107
|
-
const config =
|
|
106
|
+
if (!global.Odac?.View?.EarlyHints) {
|
|
107
|
+
const config = odac.Config?.earlyHints
|
|
108
108
|
this.#earlyHints = new EarlyHints(config)
|
|
109
109
|
this.#earlyHints.init()
|
|
110
110
|
|
|
111
|
-
if (!global.
|
|
112
|
-
if (!global.
|
|
113
|
-
global.
|
|
111
|
+
if (!global.Odac) global.Odac = {}
|
|
112
|
+
if (!global.Odac.View) global.Odac.View = {}
|
|
113
|
+
global.Odac.View.EarlyHints = this.#earlyHints
|
|
114
114
|
} else {
|
|
115
|
-
this.#earlyHints = global.
|
|
115
|
+
this.#earlyHints = global.Odac.View.EarlyHints
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
118
|
|
|
@@ -123,24 +123,24 @@ class View {
|
|
|
123
123
|
|
|
124
124
|
// - PRINT VIEW
|
|
125
125
|
async print() {
|
|
126
|
-
if (this.#
|
|
126
|
+
if (this.#odac.Request.res.finished) return
|
|
127
127
|
|
|
128
|
-
const routePath = this.#
|
|
128
|
+
const routePath = this.#odac.Request.req.url.split('?')[0]
|
|
129
129
|
|
|
130
130
|
// Handle AJAX load requests
|
|
131
|
-
if (this.#
|
|
131
|
+
if (this.#odac.Request.isAjaxLoad === true && this.#odac.Request.ajaxLoad && this.#odac.Request.ajaxLoad.length > 0) {
|
|
132
132
|
let output = {}
|
|
133
133
|
let variables = {}
|
|
134
134
|
|
|
135
135
|
// Collect variables marked for AJAX
|
|
136
|
-
for (let key in this.#
|
|
137
|
-
if (this.#
|
|
138
|
-
variables[key] = this.#
|
|
136
|
+
for (let key in this.#odac.Request.variables) {
|
|
137
|
+
if (this.#odac.Request.variables[key].ajax) {
|
|
138
|
+
variables[key] = this.#odac.Request.variables[key].value
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
// Render requested elements
|
|
143
|
-
for (let element of this.#
|
|
143
|
+
for (let element of this.#odac.Request.ajaxLoad) {
|
|
144
144
|
if (this.#part[element]) {
|
|
145
145
|
let viewPath = this.#part[element]
|
|
146
146
|
if (viewPath.includes('.')) viewPath = viewPath.replace(/\./g, '/')
|
|
@@ -151,14 +151,14 @@ class View {
|
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
const currentSkeleton = this.#part.skeleton || 'main'
|
|
154
|
-
const clientSkeleton = this.#
|
|
154
|
+
const clientSkeleton = this.#odac.Request.clientSkeleton
|
|
155
155
|
const skeletonChanged = clientSkeleton && clientSkeleton !== currentSkeleton
|
|
156
156
|
|
|
157
|
-
this.#
|
|
158
|
-
this.#
|
|
159
|
-
this.#
|
|
157
|
+
this.#odac.Request.header('Content-Type', 'application/json')
|
|
158
|
+
this.#odac.Request.header('X-Odac-Page', this.#odac.Request.page || '')
|
|
159
|
+
this.#odac.Request.header('Vary', 'X-Odac')
|
|
160
160
|
|
|
161
|
-
this.#
|
|
161
|
+
this.#odac.Request.end({
|
|
162
162
|
output: output,
|
|
163
163
|
variables: variables,
|
|
164
164
|
skeletonChanged: skeletonChanged
|
|
@@ -171,7 +171,7 @@ class View {
|
|
|
171
171
|
if (this.#part.skeleton && fs.existsSync(`./skeleton/${this.#part.skeleton}.html`)) {
|
|
172
172
|
result = fs.readFileSync(`./skeleton/${this.#part.skeleton}.html`, 'utf8')
|
|
173
173
|
|
|
174
|
-
// Add data-
|
|
174
|
+
// Add data-odac-navigate to content wrapper for auto-navigation
|
|
175
175
|
result = this.#addNavigateAttribute(result)
|
|
176
176
|
|
|
177
177
|
for (let key in this.#part) {
|
|
@@ -183,8 +183,8 @@ class View {
|
|
|
183
183
|
}
|
|
184
184
|
}
|
|
185
185
|
if (this.#part.all) {
|
|
186
|
-
let parts = result.match(/{{.*?}}/g).map(part => part.replace(/{{|}}/g, '').trim())
|
|
187
|
-
if (parts)
|
|
186
|
+
let parts = (result.match(/{{.*?}}/g) || []).map(part => part.replace(/{{|}}/g, '').trim())
|
|
187
|
+
if (parts.length > 0)
|
|
188
188
|
for (let part of parts) {
|
|
189
189
|
part = part.trim()
|
|
190
190
|
let file = this.#part.all.split('.')
|
|
@@ -198,7 +198,7 @@ class View {
|
|
|
198
198
|
}
|
|
199
199
|
|
|
200
200
|
if (result) {
|
|
201
|
-
const hasEarlyHints = this.#
|
|
201
|
+
const hasEarlyHints = this.#odac.Request.hasEarlyHints()
|
|
202
202
|
|
|
203
203
|
if (!hasEarlyHints) {
|
|
204
204
|
const detectedResources = this.#earlyHints.extractFromHtml(result)
|
|
@@ -209,27 +209,27 @@ class View {
|
|
|
209
209
|
}
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
-
this.#
|
|
213
|
-
this.#
|
|
212
|
+
this.#odac.Request.header('Content-Type', 'text/html')
|
|
213
|
+
this.#odac.Request.end(result)
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
-
#
|
|
216
|
+
#parseOdacTag(content) {
|
|
217
217
|
// Parse backend comments
|
|
218
|
-
// Multi-line: <!--
|
|
219
|
-
// Single-line: <!--
|
|
220
|
-
content = content.replace(/<!--
|
|
218
|
+
// Multi-line: <!--odac ... odac-->
|
|
219
|
+
// Single-line: <!--odac ... -->
|
|
220
|
+
content = content.replace(/<!--odac([\s\S]*?)(?:odac-->|-->)/g, () => {
|
|
221
221
|
return ''
|
|
222
222
|
})
|
|
223
223
|
|
|
224
|
-
// Parse <script:
|
|
225
|
-
content = content.replace(/<script:
|
|
226
|
-
return `<
|
|
224
|
+
// Parse <script:odac> tags (IDE-friendly JavaScript with backend execution)
|
|
225
|
+
content = content.replace(/<script:odac([^>]*)>([\s\S]*?)<\/script:odac>/g, (fullMatch, attributes, jsContent) => {
|
|
226
|
+
return `<odac:js>${jsContent}</odac:js>`
|
|
227
227
|
})
|
|
228
228
|
|
|
229
|
-
content = content.replace(/<
|
|
230
|
-
content = content.replace(/<
|
|
229
|
+
content = content.replace(/<odac:else\s*\/>/g, '<odac:else>')
|
|
230
|
+
content = content.replace(/<odac:elseif\s+([^>]*?)\/>/g, '<odac:elseif $1>')
|
|
231
231
|
|
|
232
|
-
content = content.replace(/<
|
|
232
|
+
content = content.replace(/<odac([^>]*?)\/>/g, (fullMatch, attributes) => {
|
|
233
233
|
attributes = attributes.trim()
|
|
234
234
|
|
|
235
235
|
const attrs = {}
|
|
@@ -255,9 +255,9 @@ class View {
|
|
|
255
255
|
|
|
256
256
|
let depth = 0
|
|
257
257
|
let maxDepth = 10
|
|
258
|
-
while (depth < maxDepth && content.includes('<
|
|
258
|
+
while (depth < maxDepth && content.includes('<odac')) {
|
|
259
259
|
const before = content
|
|
260
|
-
content = content.replace(/<
|
|
260
|
+
content = content.replace(/<odac([^>]*)>((?:(?!<odac)[\s\S])*?)<\/odac>/g, (fullMatch, attributes, innerContent) => {
|
|
261
261
|
attributes = attributes.trim()
|
|
262
262
|
innerContent = innerContent.trim()
|
|
263
263
|
|
|
@@ -288,7 +288,7 @@ class View {
|
|
|
288
288
|
if (variable.startsWith("'") && variable.endsWith("'")) {
|
|
289
289
|
placeholders.push(variable)
|
|
290
290
|
} else {
|
|
291
|
-
placeholders.push(`
|
|
291
|
+
placeholders.push(`Odac.Var(await ${variable}).html().replace(/\\n/g, "<br>")`)
|
|
292
292
|
}
|
|
293
293
|
return `%s${placeholderIndex++}`
|
|
294
294
|
})
|
|
@@ -322,20 +322,20 @@ class View {
|
|
|
322
322
|
let content = fs.readFileSync(file, 'utf8')
|
|
323
323
|
|
|
324
324
|
if (this.#cache[file]?.mtime !== mtime) {
|
|
325
|
-
content = Form.parse(content, this.#
|
|
325
|
+
content = Form.parse(content, this.#odac)
|
|
326
326
|
|
|
327
327
|
const jsBlocks = []
|
|
328
|
-
content = content.replace(/<script:
|
|
329
|
-
const placeholder = `
|
|
328
|
+
content = content.replace(/<script:odac([^>]*)>([\s\S]*?)<\/script:odac>/g, (match, attrs, jsContent) => {
|
|
329
|
+
const placeholder = `___ODAC_JS_BLOCK_${jsBlocks.length}___`
|
|
330
330
|
jsBlocks.push(jsContent)
|
|
331
|
-
return `<script:
|
|
331
|
+
return `<script:odac${attrs}>${placeholder}</script:odac>`
|
|
332
332
|
})
|
|
333
333
|
|
|
334
|
-
content = this.#
|
|
335
|
-
content = content.replace(/`/g, '
|
|
334
|
+
content = this.#parseOdacTag(content)
|
|
335
|
+
content = content.replace(/`/g, '\\\\`').replace(/\$\{/g, '\\\\${')
|
|
336
336
|
|
|
337
337
|
jsBlocks.forEach((jsContent, index) => {
|
|
338
|
-
content = content.replace(`
|
|
338
|
+
content = content.replace(`___ODAC_JS_BLOCK_${index}___`, jsContent)
|
|
339
339
|
})
|
|
340
340
|
|
|
341
341
|
let result = 'html += `\n' + content + '\n`'
|
|
@@ -345,11 +345,11 @@ class View {
|
|
|
345
345
|
let func = this.#functions[key]
|
|
346
346
|
let matches = func.close
|
|
347
347
|
? result.match(new RegExp(`${key}[\\s\\S]*?${func.close}`, 'g'))
|
|
348
|
-
: result.match(new RegExp(`<
|
|
348
|
+
: result.match(new RegExp(`<odac:${key}(?:\\s+[^>]*?(?:"[^"]*"|'[^']*'|[^"'>])*)?>`, 'g'))
|
|
349
349
|
if (!matches) continue
|
|
350
350
|
for (let match of matches) {
|
|
351
351
|
let matchForParsing = match
|
|
352
|
-
if (!func.close) matchForParsing = matchForParsing.replace(/^<
|
|
352
|
+
if (!func.close) matchForParsing = matchForParsing.replace(/^<odac:/, '').replace(/>$/, '')
|
|
353
353
|
const attrRegex = /(\w+)(?:=(["'])((?:(?!\2).)*)\2|=([^\s>]+))?/g
|
|
354
354
|
let attrMatch
|
|
355
355
|
const args = []
|
|
@@ -401,10 +401,7 @@ class View {
|
|
|
401
401
|
result = result.replace(match, fun + match.substring(key.length, match.length - func.close.length) + func.end)
|
|
402
402
|
} else {
|
|
403
403
|
result = result.replace(match, (func.replace ? `<${[func.replace, att].join(' ')}>` : '') + '`; ' + fun + ' html += `')
|
|
404
|
-
result = result.replace(
|
|
405
|
-
`</candy:${key}>`,
|
|
406
|
-
'`; ' + (func.end ?? '}') + ' html += `' + (func.replace ? `</${func.replace}>` : '')
|
|
407
|
-
)
|
|
404
|
+
result = result.replace(`</odac:${key}>`, '`; ' + (func.end ?? '}') + ' html += `' + (func.replace ? `</${func.replace}>` : ''))
|
|
408
405
|
}
|
|
409
406
|
}
|
|
410
407
|
}
|
|
@@ -412,21 +409,21 @@ class View {
|
|
|
412
409
|
if (!fs.existsSync(CACHE_DIR)) fs.mkdirSync(CACHE_DIR, {recursive: true})
|
|
413
410
|
fs.writeFileSync(
|
|
414
411
|
`${CACHE_DIR}/${cache}`,
|
|
415
|
-
`module.exports = async (
|
|
412
|
+
`module.exports = async (Odac, get, __) => {\nlet html = '';\n${result}\nreturn html.trim()\n}`
|
|
416
413
|
)
|
|
417
414
|
delete require.cache[require.resolve(`${__dir}/${CACHE_DIR}/${cache}`)]
|
|
418
|
-
if (!
|
|
419
|
-
if (!
|
|
420
|
-
|
|
415
|
+
if (!Odac.View) Odac.View = {}
|
|
416
|
+
if (!Odac.View.cache) Odac.View.cache = {}
|
|
417
|
+
Odac.View.cache[file] = {
|
|
421
418
|
mtime: mtime,
|
|
422
419
|
cache: cache
|
|
423
420
|
}
|
|
424
421
|
}
|
|
425
422
|
try {
|
|
426
|
-
return await require(`${__dir}/${CACHE_DIR}/${
|
|
427
|
-
this.#
|
|
428
|
-
key => this.#
|
|
429
|
-
(...args) => this.#
|
|
423
|
+
return await require(`${__dir}/${CACHE_DIR}/${Odac.View.cache[file].cache}`)(
|
|
424
|
+
this.#odac,
|
|
425
|
+
key => this.#odac.Request.get(key),
|
|
426
|
+
(...args) => this.#odac.Lang.get(...args)
|
|
430
427
|
)
|
|
431
428
|
} catch (e) {
|
|
432
429
|
let stackLine = e.stack.split('\n')[1].match(/:(\d+):\d+/)
|
|
@@ -441,8 +438,8 @@ class View {
|
|
|
441
438
|
if (args.length === 1 && typeof args[0] === 'object') for (let key in args[0]) this.#part[key] = args[0][key]
|
|
442
439
|
else if (args.length === 2) this.#part[args[0]] = args[1]
|
|
443
440
|
|
|
444
|
-
if (!this.#
|
|
445
|
-
this.#
|
|
441
|
+
if (!this.#odac.Request.page) {
|
|
442
|
+
this.#odac.Request.page = this.#part.content || this.#part.all || ''
|
|
446
443
|
}
|
|
447
444
|
|
|
448
445
|
this.#sendEarlyHintsIfAvailable()
|
|
@@ -457,21 +454,21 @@ class View {
|
|
|
457
454
|
|
|
458
455
|
#addNavigateAttribute(skeleton) {
|
|
459
456
|
skeleton = skeleton.replace(/(<[^>]+>)(\s*\{\{\s*CONTENT\s*\}\})/, (match, openTag, content) => {
|
|
460
|
-
if (openTag.includes('data-
|
|
461
|
-
const tagWithAttr = openTag.slice(0, -1) + ' data-
|
|
457
|
+
if (openTag.includes('data-odac-navigate')) return match
|
|
458
|
+
const tagWithAttr = openTag.slice(0, -1) + ' data-odac-navigate="content">'
|
|
462
459
|
return tagWithAttr + content
|
|
463
460
|
})
|
|
464
461
|
|
|
465
462
|
const skeletonName = this.#part.skeleton || 'main'
|
|
466
|
-
const pageName = this.#
|
|
463
|
+
const pageName = this.#odac.Request.page || ''
|
|
467
464
|
|
|
468
465
|
skeleton = skeleton.replace(/<html([^>]*)>/, (match, attrs) => {
|
|
469
466
|
const updates = []
|
|
470
|
-
if (!attrs.includes('data-
|
|
471
|
-
updates.push(`data-
|
|
467
|
+
if (!attrs.includes('data-odac-skeleton')) {
|
|
468
|
+
updates.push(`data-odac-skeleton="${skeletonName}"`)
|
|
472
469
|
}
|
|
473
|
-
if (!attrs.includes('data-
|
|
474
|
-
updates.push(`data-
|
|
470
|
+
if (!attrs.includes('data-odac-page')) {
|
|
471
|
+
updates.push(`data-odac-page="${pageName}"`)
|
|
475
472
|
}
|
|
476
473
|
if (updates.length === 0) return match
|
|
477
474
|
return `<html${attrs} ${updates.join(' ')}>`
|
|
@@ -481,9 +478,9 @@ class View {
|
|
|
481
478
|
}
|
|
482
479
|
|
|
483
480
|
#sendEarlyHintsIfAvailable() {
|
|
484
|
-
if (this.#
|
|
481
|
+
if (this.#odac.Request.res.headersSent) return
|
|
485
482
|
|
|
486
|
-
const routePath = this.#
|
|
483
|
+
const routePath = this.#odac.Request.req.url.split('?')[0]
|
|
487
484
|
const viewPaths = []
|
|
488
485
|
|
|
489
486
|
if (this.#part.skeleton) {
|
|
@@ -505,7 +502,7 @@ class View {
|
|
|
505
502
|
}
|
|
506
503
|
|
|
507
504
|
if (hints && hints.length > 0) {
|
|
508
|
-
this.#
|
|
505
|
+
this.#odac.Request.setEarlyHints(hints)
|
|
509
506
|
}
|
|
510
507
|
}
|
|
511
508
|
}
|