@newlogic-digital/core 2.0.0-alpha.1 → 2.0.0-alpha.11
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/index.js +107 -49
- package/latte/JsonFilter.js +7 -0
- package/package.json +13 -9
- package/src/minify.js +22 -0
- package/src/twig.js +174 -0
- /package/{prism.js → src/prism.js} +0 -0
package/index.js
CHANGED
@@ -1,16 +1,20 @@
|
|
1
|
-
import fs from 'fs'
|
2
|
-
import os from 'os'
|
3
|
-
import { dirname, resolve, join } from 'path'
|
1
|
+
import fs from 'node:fs'
|
2
|
+
import os from 'node:os'
|
3
|
+
import { dirname, resolve, join, relative } from 'node:path'
|
4
4
|
import postHtml from 'posthtml'
|
5
5
|
import vituum from 'vituum'
|
6
6
|
import posthtml from '@vituum/vite-plugin-posthtml'
|
7
7
|
import latte from '@vituum/vite-plugin-latte'
|
8
|
+
import twig from '@vituum/vite-plugin-twig'
|
8
9
|
import juice from '@vituum/vite-plugin-juice'
|
9
10
|
import send from '@vituum/vite-plugin-send'
|
10
11
|
import tailwindcss from '@vituum/vite-plugin-tailwindcss'
|
11
12
|
import { getPackageInfo, merge } from 'vituum/utils/common.js'
|
12
|
-
import
|
13
|
-
import
|
13
|
+
import highlight from './src/prism.js'
|
14
|
+
import twigOptions from './src/twig.js'
|
15
|
+
import FastGlob from 'fast-glob'
|
16
|
+
import fse from 'fs-extra'
|
17
|
+
import pc from 'picocolors'
|
14
18
|
|
15
19
|
const { name } = getPackageInfo(import.meta.url)
|
16
20
|
|
@@ -35,39 +39,34 @@ const posthtmlPrism = {
|
|
35
39
|
}
|
36
40
|
}
|
37
41
|
|
38
|
-
const parseMinifyHtml = async (input, name) => {
|
39
|
-
const minify = await minifier.minify(input, {
|
40
|
-
collapseWhitespace: true,
|
41
|
-
collapseInlineTagWhitespace: false,
|
42
|
-
minifyCSS: true,
|
43
|
-
removeAttributeQuotes: true,
|
44
|
-
quoteCharacter: '\'',
|
45
|
-
minifyJS: true
|
46
|
-
})
|
47
|
-
|
48
|
-
if (name) {
|
49
|
-
return JSON.stringify({
|
50
|
-
[name]: minify
|
51
|
-
})
|
52
|
-
} else {
|
53
|
-
return JSON.stringify(minify)
|
54
|
-
}
|
55
|
-
}
|
56
|
-
|
57
42
|
/**
|
58
43
|
* @type {import('@newlogic-digital/core/types').PluginUserConfig}
|
59
44
|
*/
|
60
45
|
const defaultOptions = {
|
46
|
+
mode: null,
|
61
47
|
cert: 'localhost',
|
48
|
+
format: ['latte'],
|
62
49
|
emails: {
|
63
50
|
outputDir: resolve(process.cwd(), 'public/email'),
|
64
51
|
appDir: resolve(process.cwd(), 'app/Templates/Emails')
|
65
52
|
},
|
66
|
-
vituum: {
|
67
|
-
|
68
|
-
|
53
|
+
vituum: {
|
54
|
+
pages: {
|
55
|
+
dir: './src/views'
|
56
|
+
}
|
57
|
+
},
|
58
|
+
posthtml: {
|
59
|
+
root: resolve(process.cwd(), 'src')
|
60
|
+
},
|
61
|
+
juice: {
|
62
|
+
paths: ['src/views/email']
|
63
|
+
},
|
69
64
|
tailwindcss: {},
|
70
|
-
send: {
|
65
|
+
send: {
|
66
|
+
host: 'smtp.newlogic.cz',
|
67
|
+
from: 'noreply@newlogic.cz',
|
68
|
+
user: 'noreply@newlogic.cz'
|
69
|
+
},
|
71
70
|
latte: {
|
72
71
|
renderTransformedHtml: (filename) => dirname(filename).endsWith('email'),
|
73
72
|
globals: {
|
@@ -80,56 +79,115 @@ const defaultOptions = {
|
|
80
79
|
}
|
81
80
|
},
|
82
81
|
filters: {
|
83
|
-
json:
|
84
|
-
return await parseMinifyHtml(input, name)
|
85
|
-
},
|
82
|
+
json: resolve(process.cwd(), 'node_modules/@newlogic-digital/core/latte/JsonFilter.js'),
|
86
83
|
code: 'node_modules/@newlogic-digital/core/latte/CodeFilter.php'
|
87
84
|
},
|
88
|
-
ignoredPaths: ['**/views/email/**/!(*.test).latte']
|
89
|
-
}
|
85
|
+
ignoredPaths: ['**/views/email/**/!(*.test).latte', '**/emails/!(*.test).latte']
|
86
|
+
},
|
87
|
+
twig: twigOptions
|
90
88
|
}
|
91
89
|
|
92
90
|
/**
|
93
91
|
* @param {import('@newlogic-digital/core/types').PluginUserConfig} options
|
94
|
-
* @returns import('vite').Plugin
|
92
|
+
* @returns [import('vite').Plugin]
|
95
93
|
*/
|
96
94
|
const plugin = (options = {}) => {
|
97
95
|
options = merge(defaultOptions, options)
|
98
96
|
|
97
|
+
const templatesPlugins = []
|
98
|
+
|
99
|
+
if (options.format.includes('twig')) {
|
100
|
+
templatesPlugins.push(twig(options.twig))
|
101
|
+
}
|
102
|
+
|
103
|
+
if (options.format.includes('latte')) {
|
104
|
+
templatesPlugins.push(latte(options.latte))
|
105
|
+
}
|
106
|
+
|
99
107
|
const plugins = [
|
100
108
|
vituum(options.vituum),
|
101
109
|
tailwindcss(options.tailwindcss),
|
102
110
|
posthtml(options.posthtml),
|
103
|
-
|
111
|
+
...templatesPlugins,
|
104
112
|
juice(options.juice),
|
105
113
|
send(options.send),
|
106
114
|
posthtmlPrism
|
107
115
|
]
|
108
116
|
|
109
|
-
return {
|
117
|
+
return [{
|
110
118
|
name,
|
111
119
|
enforce: 'pre',
|
112
|
-
config (userConfig) {
|
113
|
-
|
114
|
-
userConfig.plugins = plugins
|
115
|
-
} else if (userConfig.plugins) {
|
116
|
-
userConfig.plugins = plugins.concat(...userConfig.plugins)
|
117
|
-
}
|
118
|
-
|
119
|
-
if (
|
120
|
-
userConfig?.server?.https !== false &&
|
120
|
+
config (userConfig, userEnv) {
|
121
|
+
const isHttps = userConfig?.server?.https !== false &&
|
121
122
|
fs.existsSync(join(os.homedir(), `.ssh/${options.cert}.pem`)) &&
|
122
123
|
fs.existsSync(join(os.homedir(), `.ssh/${options.cert}-key.pem`))
|
123
|
-
|
124
|
-
|
125
|
-
|
124
|
+
|
125
|
+
let defaultInput = [
|
126
|
+
'./src/styles/*.{css,pcss,scss,sass,less,styl,stylus}',
|
127
|
+
'./src/scripts/*.{js,ts,mjs}'
|
128
|
+
]
|
129
|
+
|
130
|
+
if (!options.mode) {
|
131
|
+
options.mode = userEnv.mode
|
132
|
+
}
|
133
|
+
|
134
|
+
if (options.mode === 'development') {
|
135
|
+
defaultInput = [
|
136
|
+
'./src/views/**/*.{json,latte,twig,liquid,njk,hbs,pug,html}',
|
137
|
+
'!./src/views/**/*.{latte,twig,liquid,njk,hbs,pug,html}.json',
|
138
|
+
'./src/styles/*.{css,pcss,scss,sass,less,styl,stylus}',
|
139
|
+
'./src/scripts/*.{js,ts,mjs}'
|
140
|
+
]
|
141
|
+
}
|
142
|
+
|
143
|
+
if (options.mode === 'emails') {
|
144
|
+
userEnv.mode = 'production'
|
145
|
+
|
146
|
+
defaultInput = [
|
147
|
+
'./src/views/email/**/*.{json,latte,twig,liquid,njk,hbs,pug,html}',
|
148
|
+
'!./src/views/email/**/*.{latte,twig,liquid,njk,hbs,pug,html}.json'
|
149
|
+
]
|
150
|
+
}
|
151
|
+
|
152
|
+
userConfig.build = Object.assign({
|
153
|
+
manifest: true,
|
154
|
+
emptyOutDir: false,
|
155
|
+
modulePreload: false,
|
156
|
+
assetsInlineLimit: 0,
|
157
|
+
outDir: resolve(userConfig.root ?? process.cwd(), 'public'),
|
158
|
+
rollupOptions: {
|
159
|
+
input: defaultInput
|
160
|
+
}
|
161
|
+
}, userConfig.build ?? {})
|
162
|
+
|
163
|
+
userConfig.server = Object.assign({
|
164
|
+
host: true,
|
165
|
+
fsServe: {
|
166
|
+
strict: false
|
167
|
+
},
|
168
|
+
https: isHttps
|
169
|
+
? {
|
126
170
|
key: fs.readFileSync(join(os.homedir(), `.ssh/${options.cert}-key.pem`)),
|
127
171
|
cert: fs.readFileSync(join(os.homedir(), `.ssh/${options.cert}.pem`))
|
128
172
|
}
|
173
|
+
: false
|
174
|
+
}, userConfig.server ?? {})
|
175
|
+
},
|
176
|
+
writeBundle: async () => {
|
177
|
+
if (options.mode === 'emails') {
|
178
|
+
const emails = FastGlob.sync(`${resolve(process.cwd(), options.emails.outputDir)}/**`).filter(entry => !entry.endsWith('test.html'))
|
179
|
+
const emailsProd = emails.map(path => {
|
180
|
+
return path.replace(resolve(process.cwd(), options.emails.outputDir), resolve(process.cwd(), options.emails.appDir)).replace('.html', '.latte')
|
129
181
|
})
|
182
|
+
|
183
|
+
await Promise.all(emails.map((file, i) =>
|
184
|
+
fse.move(file, emailsProd[i], { overwrite: true })
|
185
|
+
))
|
186
|
+
|
187
|
+
console.info(`${pc.cyan('@newlogic-digital/core')} ${pc.green(`all email files were moved to ${relative(process.cwd(), options.emails.appDir)}`)}`)
|
130
188
|
}
|
131
189
|
}
|
132
|
-
}
|
190
|
+
}, ...plugins]
|
133
191
|
}
|
134
192
|
|
135
193
|
export default plugin
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@newlogic-digital/core",
|
3
3
|
"type": "module",
|
4
|
-
"version": "2.0.0-alpha.
|
4
|
+
"version": "2.0.0-alpha.11",
|
5
5
|
"main": "index.js",
|
6
6
|
"author": "New Logic Studio s.r.o.",
|
7
7
|
"description": "Set of tools that can be used to create modern web applications",
|
@@ -12,20 +12,24 @@
|
|
12
12
|
},
|
13
13
|
"dependencies": {
|
14
14
|
"@vituum/vite-plugin-posthtml": "^1.0.0-alpha.3",
|
15
|
-
"@vituum/vite-plugin-juice": "^1.0.0-alpha.
|
16
|
-
"@vituum/vite-plugin-latte": "^1.0.0-alpha.
|
17
|
-
"@vituum/vite-plugin-
|
18
|
-
"@vituum/vite-plugin-
|
19
|
-
"vituum": "^1.0.0-alpha.
|
15
|
+
"@vituum/vite-plugin-juice": "^1.0.0-alpha.2",
|
16
|
+
"@vituum/vite-plugin-latte": "^1.0.0-alpha.8",
|
17
|
+
"@vituum/vite-plugin-twig": "^1.0.0-alpha.6",
|
18
|
+
"@vituum/vite-plugin-tailwindcss": "^1.0.0-alpha.2",
|
19
|
+
"@vituum/vite-plugin-send": "^1.0.0-alpha.2",
|
20
|
+
"vituum": "^1.0.0-alpha.18",
|
20
21
|
"posthtml": "^0.16.6",
|
21
22
|
"posthtml-prism": "^2.0.0",
|
22
23
|
"prismjs": "^1.29.0",
|
23
24
|
"html-minifier-terser": "^7.2.0",
|
24
|
-
"lodash": "^4.17.21"
|
25
|
+
"lodash": "^4.17.21",
|
26
|
+
"fast-glob": "^3.2.12",
|
27
|
+
"fs-extra": "^11.1.1",
|
28
|
+
"picocolors": "^1.0.0"
|
25
29
|
},
|
26
30
|
"devDependencies": {
|
27
31
|
"@types/node": "^20.3.1",
|
28
|
-
"eslint": "^8.
|
32
|
+
"eslint": "^8.43.0",
|
29
33
|
"eslint-config-standard": "^17.1.0",
|
30
34
|
"typescript": "^5.1.3",
|
31
35
|
"vite": "^4.3.9"
|
@@ -33,7 +37,7 @@
|
|
33
37
|
"files": [
|
34
38
|
"latte",
|
35
39
|
"index.js",
|
36
|
-
"
|
40
|
+
"src"
|
37
41
|
],
|
38
42
|
"engines": {
|
39
43
|
"node": ">=16.0.0",
|
package/src/minify.js
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
import minifier from 'html-minifier-terser'
|
2
|
+
|
3
|
+
const parseMinifyHtml = async (input, name) => {
|
4
|
+
const minify = await minifier.minify(input, {
|
5
|
+
collapseWhitespace: true,
|
6
|
+
collapseInlineTagWhitespace: false,
|
7
|
+
minifyCSS: true,
|
8
|
+
removeAttributeQuotes: true,
|
9
|
+
quoteCharacter: '\'',
|
10
|
+
minifyJS: true
|
11
|
+
})
|
12
|
+
|
13
|
+
if (name) {
|
14
|
+
return JSON.stringify({
|
15
|
+
[name]: minify
|
16
|
+
})
|
17
|
+
} else {
|
18
|
+
return JSON.stringify(minify)
|
19
|
+
}
|
20
|
+
}
|
21
|
+
|
22
|
+
export default parseMinifyHtml
|
package/src/twig.js
ADDED
@@ -0,0 +1,174 @@
|
|
1
|
+
import fs from 'fs'
|
2
|
+
import { resolve } from 'path'
|
3
|
+
import parseMinifyHtml from './minify.js'
|
4
|
+
|
5
|
+
const wrapPreCode = (code, lang) => {
|
6
|
+
return `<pre class="language-${lang}"><code class="language-${lang}">${code}</code></pre>`
|
7
|
+
}
|
8
|
+
|
9
|
+
const stripIndent = (string) => {
|
10
|
+
const indent = () => {
|
11
|
+
const match = string.match(/^[ \t]*(?=\S)/gm)
|
12
|
+
|
13
|
+
if (!match) {
|
14
|
+
return 0
|
15
|
+
}
|
16
|
+
|
17
|
+
return match.reduce((r, a) => Math.min(r, a.length), Infinity)
|
18
|
+
}
|
19
|
+
|
20
|
+
if (indent() === 0) {
|
21
|
+
return string
|
22
|
+
}
|
23
|
+
|
24
|
+
const regex = new RegExp(`^[ \\t]{${indent()}}`, 'gm')
|
25
|
+
|
26
|
+
return string.replace(regex, '')
|
27
|
+
}
|
28
|
+
|
29
|
+
export default {
|
30
|
+
namespaces: {
|
31
|
+
src: resolve(process.cwd(), 'src'),
|
32
|
+
templates: resolve(process.cwd(), 'src/templates')
|
33
|
+
},
|
34
|
+
functions: {
|
35
|
+
pages: () => {
|
36
|
+
return fs.readdirSync('src/views').filter(file => fs.statSync('src/views/' + file).isFile())
|
37
|
+
},
|
38
|
+
fetch: (data) => {
|
39
|
+
if (typeof data !== 'undefined') {
|
40
|
+
if (data.indexOf('http') > -1) {
|
41
|
+
return data
|
42
|
+
} else {
|
43
|
+
let slash = data.indexOf('/') + 1
|
44
|
+
if (slash > 1) {
|
45
|
+
slash = 0
|
46
|
+
}
|
47
|
+
|
48
|
+
return fs.readFileSync(process.cwd() + '/' + data.substring(slash, data.length), 'utf8').toString()
|
49
|
+
}
|
50
|
+
}
|
51
|
+
},
|
52
|
+
randomColor: () => {
|
53
|
+
return '#' + Math.random().toString(16).slice(2, 8)
|
54
|
+
},
|
55
|
+
placeholder: (width, height) => {
|
56
|
+
const colors = ['333', '444', '666', '222', '777', '888', '111']
|
57
|
+
return 'https://via.placeholder.com/' + width + 'x' + height + '/' + colors[Math.floor(Math.random() * colors.length)] + '.webp'
|
58
|
+
},
|
59
|
+
lazy: (width, height) => {
|
60
|
+
const svg = encodeURIComponent(stripIndent('<svg width="' + width + '" height="' + height + '" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ' + width + ' ' + height + '"></svg>'))
|
61
|
+
|
62
|
+
return 'data:image/svg+xml;charset=UTF-8,' + svg
|
63
|
+
},
|
64
|
+
ratio: (width, height) => {
|
65
|
+
return (height / width) * 100
|
66
|
+
}
|
67
|
+
},
|
68
|
+
filters: {
|
69
|
+
asset: (url) => {
|
70
|
+
return url
|
71
|
+
},
|
72
|
+
rem: (value) => {
|
73
|
+
return `${value / 16}rem`
|
74
|
+
},
|
75
|
+
encode64: (path) => {
|
76
|
+
const svg = encodeURIComponent(stripIndent(path))
|
77
|
+
|
78
|
+
return 'data:image/svg+xml;charset=UTF-8,' + svg
|
79
|
+
},
|
80
|
+
exists: (path) => {
|
81
|
+
if (path.indexOf('/') === 0) {
|
82
|
+
path = path.slice(1)
|
83
|
+
}
|
84
|
+
|
85
|
+
return fs.existsSync(resolve(process.cwd(), path))
|
86
|
+
},
|
87
|
+
tel: (value) => {
|
88
|
+
return value.replace(/\s+/g, '').replace('(', '').replace(')', '')
|
89
|
+
}
|
90
|
+
},
|
91
|
+
extensions: [
|
92
|
+
(Twig) => {
|
93
|
+
Twig.exports.extendTag({
|
94
|
+
type: 'json',
|
95
|
+
regex: /^json\s+(.+)$|^json$/,
|
96
|
+
next: ['endjson'],
|
97
|
+
open: true,
|
98
|
+
compile: function (token) {
|
99
|
+
const expression = token.match[1] ?? '\'_null\''
|
100
|
+
|
101
|
+
token.stack = Reflect.apply(Twig.expression.compile, this, [{
|
102
|
+
type: Twig.expression.type.expression,
|
103
|
+
value: expression
|
104
|
+
}]).stack
|
105
|
+
|
106
|
+
delete token.match
|
107
|
+
return token
|
108
|
+
},
|
109
|
+
parse: async function (token, context, chain) {
|
110
|
+
const name = Reflect.apply(Twig.expression.parse, this, [token.stack, context])
|
111
|
+
const output = this.parse(token.output, context)
|
112
|
+
|
113
|
+
if (name === '_null') {
|
114
|
+
return {
|
115
|
+
chain,
|
116
|
+
output: await parseMinifyHtml(output)
|
117
|
+
}
|
118
|
+
} else {
|
119
|
+
return {
|
120
|
+
chain,
|
121
|
+
output: await parseMinifyHtml(output, name)
|
122
|
+
}
|
123
|
+
}
|
124
|
+
}
|
125
|
+
})
|
126
|
+
Twig.exports.extendTag({
|
127
|
+
type: 'endjson',
|
128
|
+
regex: /^endjson$/,
|
129
|
+
next: [],
|
130
|
+
open: false
|
131
|
+
})
|
132
|
+
},
|
133
|
+
(Twig) => {
|
134
|
+
Twig.exports.extendTag({
|
135
|
+
type: 'code',
|
136
|
+
regex: /^code\s+(.+)$/,
|
137
|
+
next: ['endcode'], // match the type of the end tag
|
138
|
+
open: true,
|
139
|
+
compile: function (token) {
|
140
|
+
const expression = token.match[1]
|
141
|
+
|
142
|
+
token.stack = Reflect.apply(Twig.expression.compile, this, [{
|
143
|
+
type: Twig.expression.type.expression,
|
144
|
+
value: expression
|
145
|
+
}]).stack
|
146
|
+
|
147
|
+
delete token.match
|
148
|
+
return token
|
149
|
+
},
|
150
|
+
parse: function (token, context, chain) {
|
151
|
+
let type = Reflect.apply(Twig.expression.parse, this, [token.stack, context])
|
152
|
+
const output = this.parse(token.output, context)
|
153
|
+
let mirror = false
|
154
|
+
|
155
|
+
if (type.includes(':mirror')) {
|
156
|
+
mirror = true
|
157
|
+
type = type.replace(':mirror', '')
|
158
|
+
}
|
159
|
+
|
160
|
+
return {
|
161
|
+
chain,
|
162
|
+
output: `${mirror ? output : ''}${wrapPreCode(output, type)}`
|
163
|
+
}
|
164
|
+
}
|
165
|
+
})
|
166
|
+
Twig.exports.extendTag({
|
167
|
+
type: 'endcode',
|
168
|
+
regex: /^endcode$/,
|
169
|
+
next: [],
|
170
|
+
open: false
|
171
|
+
})
|
172
|
+
}
|
173
|
+
]
|
174
|
+
}
|
File without changes
|