@newlogic-digital/core 2.0.0-alpha.6 → 2.0.0-alpha.8
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 +50 -10
- package/package.json +6 -3
- package/src/minify.js +22 -0
- package/src/twig.js +174 -0
package/index.js
CHANGED
@@ -1,6 +1,6 @@
|
|
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'
|
@@ -13,6 +13,9 @@ import { getPackageInfo, merge } from 'vituum/utils/common.js'
|
|
13
13
|
import parseMinifyHtml from './src/minify.js'
|
14
14
|
import highlight from './src/prism.js'
|
15
15
|
import twigOptions from './src/twig.js'
|
16
|
+
import FastGlob from 'fast-glob'
|
17
|
+
import fse from 'fs-extra'
|
18
|
+
import pc from 'picocolors'
|
16
19
|
|
17
20
|
const { name } = getPackageInfo(import.meta.url)
|
18
21
|
|
@@ -41,6 +44,7 @@ const posthtmlPrism = {
|
|
41
44
|
* @type {import('@newlogic-digital/core/types').PluginUserConfig}
|
42
45
|
*/
|
43
46
|
const defaultOptions = {
|
47
|
+
mode: null,
|
44
48
|
cert: 'localhost',
|
45
49
|
emails: {
|
46
50
|
outputDir: resolve(process.cwd(), 'public/email'),
|
@@ -106,11 +110,38 @@ const plugin = (options = {}) => {
|
|
106
110
|
return [{
|
107
111
|
name,
|
108
112
|
enforce: 'pre',
|
109
|
-
config (userConfig) {
|
113
|
+
config (userConfig, userEnv) {
|
110
114
|
const isHttps = userConfig?.server?.https !== false &&
|
111
115
|
fs.existsSync(join(os.homedir(), `.ssh/${options.cert}.pem`)) &&
|
112
116
|
fs.existsSync(join(os.homedir(), `.ssh/${options.cert}-key.pem`))
|
113
117
|
|
118
|
+
let defaultInput = [
|
119
|
+
'./src/styles/*.{css,pcss,scss,sass,less,styl,stylus}',
|
120
|
+
'./src/scripts/*.{js,ts,mjs}',
|
121
|
+
'./src/views/**/*.{json,latte,twig,liquid,njk,hbs,pug,html}',
|
122
|
+
'!./src/views/**/*.{latte,twig,liquid,njk,hbs,pug,html}.json'
|
123
|
+
]
|
124
|
+
|
125
|
+
if (!options.mode) {
|
126
|
+
options.mode = userEnv.mode
|
127
|
+
}
|
128
|
+
|
129
|
+
if (userEnv.mode === 'headless') {
|
130
|
+
userEnv.mode = 'production'
|
131
|
+
|
132
|
+
defaultInput = [
|
133
|
+
'./src/styles/*.{css,pcss,scss,sass,less,styl,stylus}',
|
134
|
+
'./src/scripts/*.{js,ts,mjs}'
|
135
|
+
]
|
136
|
+
} else if (userEnv.mode === 'emails') {
|
137
|
+
userEnv.mode = 'production'
|
138
|
+
|
139
|
+
defaultInput = [
|
140
|
+
'./src/views/email/**/*.{json,latte,twig,liquid,njk,hbs,pug,html}',
|
141
|
+
'!./src/views/email/**/*.{latte,twig,liquid,njk,hbs,pug,html}.json'
|
142
|
+
]
|
143
|
+
}
|
144
|
+
|
114
145
|
userConfig.build = Object.assign({
|
115
146
|
manifest: true,
|
116
147
|
emptyOutDir: false,
|
@@ -118,12 +149,7 @@ const plugin = (options = {}) => {
|
|
118
149
|
assetsInlineLimit: 0,
|
119
150
|
outDir: resolve(userConfig.root ?? process.cwd(), 'public'),
|
120
151
|
rollupOptions: {
|
121
|
-
input:
|
122
|
-
'./src/styles/*.{css,pcss,scss,sass,less,styl,stylus}',
|
123
|
-
'./src/scripts/*.{js,ts,mjs}',
|
124
|
-
'./src/views/**/*.{json,latte,twig,liquid,njk,hbs,pug,html}',
|
125
|
-
'!./src/views/**/*.{latte,twig,liquid,njk,hbs,pug,html}.json'
|
126
|
-
]
|
152
|
+
input: defaultInput
|
127
153
|
}
|
128
154
|
}, userConfig.build ?? {})
|
129
155
|
|
@@ -139,6 +165,20 @@ const plugin = (options = {}) => {
|
|
139
165
|
}
|
140
166
|
: false
|
141
167
|
}, userConfig.server ?? {})
|
168
|
+
},
|
169
|
+
writeBundle: async () => {
|
170
|
+
if (options.mode === 'emails') {
|
171
|
+
const emails = FastGlob.sync(`${resolve(process.cwd(), options.emails.outputDir)}/**`).filter(entry => !entry.endsWith('test.html'))
|
172
|
+
const emailsProd = emails.map(path => {
|
173
|
+
return path.replace(resolve(process.cwd(), options.emails.outputDir), resolve(process.cwd(), options.emails.appDir)).replace('.html', '.latte')
|
174
|
+
})
|
175
|
+
|
176
|
+
await Promise.all(emails.map((file, i) =>
|
177
|
+
fse.move(file, emailsProd[i], { overwrite: true })
|
178
|
+
))
|
179
|
+
|
180
|
+
console.info(`${pc.cyan('@newlogic-digital/core')} ${pc.green(`all email files were moved to ${relative(process.cwd(), options.emails.appDir)}`)}`)
|
181
|
+
}
|
142
182
|
}
|
143
183
|
}, ...plugins]
|
144
184
|
}
|
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.8",
|
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",
|
@@ -22,7 +22,10 @@
|
|
22
22
|
"posthtml-prism": "^2.0.0",
|
23
23
|
"prismjs": "^1.29.0",
|
24
24
|
"html-minifier-terser": "^7.2.0",
|
25
|
-
"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"
|
26
29
|
},
|
27
30
|
"devDependencies": {
|
28
31
|
"@types/node": "^20.3.1",
|
@@ -34,7 +37,7 @@
|
|
34
37
|
"files": [
|
35
38
|
"latte",
|
36
39
|
"index.js",
|
37
|
-
"src
|
40
|
+
"src"
|
38
41
|
],
|
39
42
|
"engines": {
|
40
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
|
+
}
|