boxwood 0.60.2 → 0.61.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/README.md +0 -358
- package/index.js +402 -4
- package/package.json +12 -37
- package/src/Bundler.js +0 -13
- package/src/Cache.js +0 -35
- package/src/Compiler.js +0 -28
- package/src/Importer.js +0 -142
- package/src/Optimizer.js +0 -96
- package/src/Scope.js +0 -20
- package/src/Statistics.js +0 -58
- package/src/bundlers/esbuild/index.js +0 -55
- package/src/bundlers/esbuild/plugins/css.js +0 -40
- package/src/bundlers/esbuild/plugins/html.js +0 -27
- package/src/bundlers/esbuild/plugins/image.js +0 -38
- package/src/bundlers/esbuild/plugins/resolve.js +0 -11
- package/src/bundlers/esbuild/plugins/yaml.js +0 -38
- package/src/bundlers/esbuild/utilities/asset.js +0 -19
- package/src/bundlers/rollup.js +0 -38
- package/src/compile.js +0 -6
- package/src/compilers/any/Compiler.js +0 -16
- package/src/compilers/compile.js +0 -17
- package/src/compilers/html/Analyzer.js +0 -24
- package/src/compilers/html/Compiler.js +0 -47
- package/src/compilers/html/Generator.js +0 -19
- package/src/compilers/html/Parser.js +0 -11
- package/src/compilers/html/Preprocessor.js +0 -130
- package/src/compilers/html/Renderer.js +0 -167
- package/src/compilers/html/Transpiler.js +0 -76
- package/src/compilers/js/Bundler.js +0 -84
- package/src/compilers/js/Compiler/index.js +0 -27
- package/src/conditions/isEqual.js +0 -5
- package/src/lexers/html.js +0 -49
- package/src/lexers/internal.js +0 -49
- package/src/linters/html/bracket.js +0 -36
- package/src/linters/html/component.js +0 -43
- package/src/linters/html/import.js +0 -35
- package/src/linters/html/index.js +0 -19
- package/src/linters/html/tag.js +0 -36
- package/src/optimizers/html.js +0 -15
- package/src/plugins/BoxModelPlugin/index.js +0 -109
- package/src/plugins/CurlyStylesPlugin/index.js +0 -39
- package/src/plugins/DataPlugin/index.js +0 -53
- package/src/plugins/InlinePlugin/css.js +0 -131
- package/src/plugins/InlinePlugin/index.js +0 -39
- package/src/plugins/InternationalizationPlugin/index.js +0 -111
- package/src/plugins/Plugin.js +0 -22
- package/src/plugins/ScopedStylesPlugin/css.js +0 -56
- package/src/plugins/ScopedStylesPlugin/html.js +0 -112
- package/src/plugins/ScopedStylesPlugin/index.js +0 -48
- package/src/plugins/SwappedStylesPlugin/index.js +0 -20
- package/src/render.js +0 -48
- package/src/tags/case.js +0 -28
- package/src/tags/catch.js +0 -17
- package/src/tags/data.js +0 -7
- package/src/tags/default.js +0 -20
- package/src/tags/doctype.js +0 -8
- package/src/tags/else.js +0 -18
- package/src/tags/elseif.js +0 -25
- package/src/tags/elseunless.js +0 -30
- package/src/tags/font.js +0 -37
- package/src/tags/for.js +0 -90
- package/src/tags/foreach.js +0 -80
- package/src/tags/if.js +0 -19
- package/src/tags/img.js +0 -25
- package/src/tags/index.js +0 -53
- package/src/tags/link.js +0 -14
- package/src/tags/script/index.js +0 -83
- package/src/tags/script/scoped.js +0 -26
- package/src/tags/slot.js +0 -9
- package/src/tags/style.js +0 -9
- package/src/tags/switch.js +0 -13
- package/src/tags/template.js +0 -22
- package/src/tags/translate.js +0 -22
- package/src/tags/translation.js +0 -38
- package/src/tags/try.js +0 -17
- package/src/tags/unless.js +0 -24
- package/src/tags/var.js +0 -32
- package/src/transpilers/css/index.js +0 -34
- package/src/transpilers/html/expression.js +0 -101
- package/src/transpilers/html/index.js +0 -189
- package/src/transpilers/html/node.js +0 -46
- package/src/transpilers/html/tags/any.js +0 -53
- package/src/transpilers/html/tags/comment.js +0 -7
- package/src/transpilers/html/tags/doctype.js +0 -6
- package/src/transpilers/html/tags/for.js +0 -30
- package/src/transpilers/html/tags/if.js +0 -55
- package/src/transpilers/html/tags/import.js +0 -7
- package/src/transpilers/html/tags/index.js +0 -13
- package/src/transpilers/html/tags/partial.js +0 -17
- package/src/transpilers/html/tags/slot.js +0 -7
- package/src/transpilers/html/tags/text.js +0 -6
- package/src/transpilers/html/tags/try.js +0 -30
- package/src/transpilers/html/tags/unless.js +0 -58
- package/src/transpilers/html/utilities/path.js +0 -9
- package/src/utilities/action.js +0 -208
- package/src/utilities/array.js +0 -80
- package/src/utilities/assets.js +0 -18
- package/src/utilities/ast.js +0 -13
- package/src/utilities/attributes.js +0 -23
- package/src/utilities/collect.js +0 -404
- package/src/utilities/conditions.js +0 -159
- package/src/utilities/convert.js +0 -359
- package/src/utilities/data.js +0 -55
- package/src/utilities/enum.js +0 -103
- package/src/utilities/errors.js +0 -96
- package/src/utilities/factory.js +0 -237
- package/src/utilities/files.js +0 -55
- package/src/utilities/filters.js +0 -140
- package/src/utilities/globals.js +0 -43
- package/src/utilities/html.js +0 -60
- package/src/utilities/inline.js +0 -200
- package/src/utilities/js.js +0 -15
- package/src/utilities/keywords.js +0 -35
- package/src/utilities/log.js +0 -31
- package/src/utilities/node.js +0 -149
- package/src/utilities/object.js +0 -8
- package/src/utilities/optimize.js +0 -268
- package/src/utilities/options.js +0 -99
- package/src/utilities/request.js +0 -13
- package/src/utilities/string.js +0 -144
- package/src/utilities/style.js +0 -15
- package/src/utilities/translations.js +0 -78
- package/src/utilities/url.js +0 -24
- package/src/vdom/browser/app.js +0 -19
- package/src/vdom/browser/diff.js +0 -104
- package/src/vdom/browser/index.js +0 -15
- package/src/vdom/browser/mount.js +0 -10
- package/src/vdom/browser/render.js +0 -66
- package/src/vdom/browser/replace.js +0 -7
- package/src/vdom/node.js +0 -10
- package/src/vdom/nodes.js +0 -116
- package/src/vdom/server/State.js +0 -21
- package/src/vdom/server/boxwood.js +0 -5
- package/src/vdom/server/index.js +0 -19
- package/src/vdom/server/render.js +0 -66
- package/src/vdom/tag.js +0 -33
- package/src/vdom/utilities/classes.js +0 -22
- package/src/vdom/utilities/css.js +0 -17
- package/src/vdom/utilities/escape.js +0 -50
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
const { duplicates } = require('pure-utilities/array')
|
|
2
|
-
const { getAssetPaths, isImageNode } = require('../../utilities/node')
|
|
3
|
-
const { isImportTag } = require('../../utilities/string')
|
|
4
|
-
const { getComponentNames } = require('../../utilities/attributes')
|
|
5
|
-
|
|
6
|
-
function verifyImports (imports, options) {
|
|
7
|
-
const warnings = []
|
|
8
|
-
const allNames = []
|
|
9
|
-
let allPaths = []
|
|
10
|
-
imports.forEach(node => {
|
|
11
|
-
if (isImportTag(node.tagName)) {
|
|
12
|
-
const names = getComponentNames(node.attributes)
|
|
13
|
-
names.forEach(name => {
|
|
14
|
-
if (allNames.includes(name)) {
|
|
15
|
-
warnings.push({ message: `Component name duplicate: ${name}`, type: 'COMPONENT_NAME_DUPLICATE' })
|
|
16
|
-
} else {
|
|
17
|
-
allNames.push(name)
|
|
18
|
-
}
|
|
19
|
-
})
|
|
20
|
-
}
|
|
21
|
-
})
|
|
22
|
-
imports.forEach(node => {
|
|
23
|
-
let assetPaths = getAssetPaths(node)
|
|
24
|
-
if (isImageNode(node, options)) {
|
|
25
|
-
assetPaths = assetPaths.filter(item => !assetPaths.includes(item))
|
|
26
|
-
}
|
|
27
|
-
allPaths = allPaths.concat(assetPaths)
|
|
28
|
-
})
|
|
29
|
-
duplicates(allPaths).forEach(duplicate => {
|
|
30
|
-
warnings.push({ message: `Component path duplicate: ${duplicate}`, type: 'COMPONENT_PATH_DUPLICATE' })
|
|
31
|
-
})
|
|
32
|
-
return warnings
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
module.exports = { verifyImports }
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const { parse } = require('../../utilities/html')
|
|
4
|
-
const { verifyBrackets } = require('./bracket')
|
|
5
|
-
const { verifyTags } = require('./tag')
|
|
6
|
-
const { verifyComponents } = require('./component')
|
|
7
|
-
const { verifyImports } = require('./import')
|
|
8
|
-
|
|
9
|
-
function lint (source, imports = [], options = {}) {
|
|
10
|
-
const tree = parse(source)
|
|
11
|
-
return [
|
|
12
|
-
...verifyBrackets(source),
|
|
13
|
-
...verifyTags(tree),
|
|
14
|
-
...verifyComponents(tree),
|
|
15
|
-
...verifyImports(imports, options)
|
|
16
|
-
]
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
module.exports = { lint }
|
package/src/linters/html/tag.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
const walk = require('himalaya-walk')
|
|
2
|
-
const { analyze } = require('./component')
|
|
3
|
-
|
|
4
|
-
const ANCHOR_TAG = 'a'
|
|
5
|
-
const IMAGE_TAG = 'img'
|
|
6
|
-
|
|
7
|
-
function isExternalUrl (url) {
|
|
8
|
-
if (!url) { return false }
|
|
9
|
-
return url.startsWith('http://') || url.startsWith('https://')
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function verifyTags (tree) {
|
|
13
|
-
const warnings = []
|
|
14
|
-
const { components } = analyze(tree)
|
|
15
|
-
walk(tree, node => {
|
|
16
|
-
if (node.tagName === ANCHOR_TAG && !components.includes(ANCHOR_TAG)) {
|
|
17
|
-
const href = node.attributes.find(attribute => attribute.key === 'href')
|
|
18
|
-
if (href && isExternalUrl(href.value)) {
|
|
19
|
-
const rel = node.attributes.find(attribute => attribute.key === 'rel')
|
|
20
|
-
if (!rel) {
|
|
21
|
-
warnings.push({ message: `${ANCHOR_TAG} tag with external href should have a rel attribute (e.g. rel="noopener")`, type: 'REL_ATTRIBUTE_MISSING' })
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (node.tagName === IMAGE_TAG && !components.includes(IMAGE_TAG)) {
|
|
27
|
-
const alt = node.attributes.find(attribute => attribute.key === 'alt' || attribute.key.startsWith('alt|'))
|
|
28
|
-
if (!alt) {
|
|
29
|
-
warnings.push({ message: `${IMAGE_TAG} tag should have an alt attribute`, type: 'ALT_ATTRIBUTE_MISSING' })
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
})
|
|
33
|
-
return warnings
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
module.exports = { verifyTags }
|
package/src/optimizers/html.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
const { minify } = require('html-minifier')
|
|
2
|
-
|
|
3
|
-
function optimize (html) {
|
|
4
|
-
return minify(html.trim(), {
|
|
5
|
-
collapseWhitespace: true,
|
|
6
|
-
conservativeCollapse: true,
|
|
7
|
-
removeComments: true,
|
|
8
|
-
sortAttributes: true,
|
|
9
|
-
sortClassName: true,
|
|
10
|
-
minifyCSS: true,
|
|
11
|
-
minifyJS: true
|
|
12
|
-
})
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
module.exports = { optimize }
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const { isCurlyTag, getTagValue, isImportTag, isPartialTag } = require('../../utilities/string')
|
|
4
|
-
const { findAttributeByKey, removeAttributeByKey } = require('../../utilities/attributes')
|
|
5
|
-
const { isNumeric } = require('pure-conditions')
|
|
6
|
-
const AbstractSyntaxTree = require('abstract-syntax-tree')
|
|
7
|
-
const Plugin = require('../Plugin')
|
|
8
|
-
const { hyphenate, capitalize } = require('pure-utilities/string')
|
|
9
|
-
|
|
10
|
-
const ATTRIBUTES = ['padding', 'margin', 'border']
|
|
11
|
-
const DIRECTIONS = ['top', 'right', 'bottom', 'left']
|
|
12
|
-
const BOX_MODEL_ATTRIBUTES = Array.from(ATTRIBUTES)
|
|
13
|
-
for (const attribute of ATTRIBUTES) {
|
|
14
|
-
for (const direction of DIRECTIONS) {
|
|
15
|
-
const boxModelAttribute = attribute.concat(capitalize(direction))
|
|
16
|
-
BOX_MODEL_ATTRIBUTES.push(boxModelAttribute, hyphenate(boxModelAttribute))
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function convert (value) {
|
|
21
|
-
if (isCurlyTag(value)) {
|
|
22
|
-
value = value.replace(/\s+/g, '')
|
|
23
|
-
value = getTagValue(value)
|
|
24
|
-
const { expression } = new AbstractSyntaxTree(`(${value})`).body[0]
|
|
25
|
-
if (expression.type === 'ObjectExpression') {
|
|
26
|
-
return AbstractSyntaxTree.serialize(expression)
|
|
27
|
-
}
|
|
28
|
-
return expression.value
|
|
29
|
-
}
|
|
30
|
-
return value
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function getStyles (attributeKey, key, value, variables = {}) {
|
|
34
|
-
if (value in variables) {
|
|
35
|
-
value = variables[value]
|
|
36
|
-
}
|
|
37
|
-
if (attributeKey === 'border') {
|
|
38
|
-
return `border: ${value};`
|
|
39
|
-
}
|
|
40
|
-
let styles = ''
|
|
41
|
-
if (typeof value === 'object') {
|
|
42
|
-
for (const property in value) {
|
|
43
|
-
if (variables[value[property]]) {
|
|
44
|
-
value[property] = variables[value[property]]
|
|
45
|
-
}
|
|
46
|
-
const suffix = getSuffix(value[property])
|
|
47
|
-
styles += `${key}-${property}: ${value[property]}${suffix} `
|
|
48
|
-
}
|
|
49
|
-
styles = styles.trim()
|
|
50
|
-
} else {
|
|
51
|
-
const suffix = getSuffix(value)
|
|
52
|
-
styles = `${hyphenate(key)}: ${value}${suffix}`
|
|
53
|
-
}
|
|
54
|
-
return styles
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function getSuffix (value) {
|
|
58
|
-
return (isNumeric(value) && Number(value) !== 0) ? 'px;' : ';'
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function appendStyles (attributes, value) {
|
|
62
|
-
const attribute = findAttributeByKey(attributes, 'style')
|
|
63
|
-
if (attribute) {
|
|
64
|
-
attribute.value += ` ${value}`
|
|
65
|
-
} else {
|
|
66
|
-
attributes.push({ key: 'style', value })
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
class BoxModelPlugin extends Plugin {
|
|
71
|
-
constructor (options = {}) {
|
|
72
|
-
super()
|
|
73
|
-
if (options.style) {
|
|
74
|
-
this.variables = options.style.variables
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
beforeprerun () {
|
|
79
|
-
this.components = []
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
prerun ({ tag, keys, fragment }) {
|
|
83
|
-
const variables = this.variables
|
|
84
|
-
function transform (key) {
|
|
85
|
-
const attribute = findAttributeByKey(fragment.attributes, key)
|
|
86
|
-
if (attribute) {
|
|
87
|
-
attribute.value = convert(attribute.value)
|
|
88
|
-
const inlineStyles = getStyles(key, attribute.key, attribute.value, variables)
|
|
89
|
-
appendStyles(fragment.attributes, inlineStyles)
|
|
90
|
-
removeAttributeByKey(fragment.attributes, key)
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
if (isImportTag(tag)) {
|
|
94
|
-
const name = keys[0]
|
|
95
|
-
this.components.push(name)
|
|
96
|
-
}
|
|
97
|
-
if (!this.components.includes(tag) && !isPartialTag(tag)) {
|
|
98
|
-
BOX_MODEL_ATTRIBUTES.forEach(transform)
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
afterprerun () {
|
|
103
|
-
this.components = []
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
run () {}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
module.exports = BoxModelPlugin
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const AbstractSyntaxTree = require('abstract-syntax-tree')
|
|
4
|
-
const Plugin = require('../Plugin')
|
|
5
|
-
const { isCurlyTag, getTagValue } = require('../../utilities/string')
|
|
6
|
-
const { convertObjectToStyleString } = require('../../utilities/style')
|
|
7
|
-
|
|
8
|
-
class CurlyStylesPlugin extends Plugin {
|
|
9
|
-
constructor () {
|
|
10
|
-
super()
|
|
11
|
-
this.scopes = []
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
prerun ({ keys, fragment, attrs }) {
|
|
15
|
-
function inline (name) {
|
|
16
|
-
if (keys.includes(name)) {
|
|
17
|
-
const attr = attrs.find(attr => attr.key === name)
|
|
18
|
-
if (isCurlyTag(attr.value)) {
|
|
19
|
-
try {
|
|
20
|
-
const expression = getTagValue(attr.value)
|
|
21
|
-
const tree = new AbstractSyntaxTree(`(${expression})`)
|
|
22
|
-
const object = AbstractSyntaxTree.serialize(tree.first('ObjectExpression'))
|
|
23
|
-
attr.key = 'style'
|
|
24
|
-
attr.value = convertObjectToStyleString(object)
|
|
25
|
-
} catch (exception) {
|
|
26
|
-
// TODO implement
|
|
27
|
-
}
|
|
28
|
-
} else {
|
|
29
|
-
attr.key = 'style'
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
inline('style')
|
|
35
|
-
inline('css')
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
module.exports = CurlyStylesPlugin
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
const Plugin = require('../Plugin')
|
|
2
|
-
const { parseData, getDataFormat } = require('../../utilities/data')
|
|
3
|
-
const { isPlainObject } = require('pure-conditions')
|
|
4
|
-
const { optimizeNode } = require('../../utilities/optimize')
|
|
5
|
-
const { createTranslationError } = require('../../utilities/errors')
|
|
6
|
-
const CODE_TAGS = ['style', 'script']
|
|
7
|
-
|
|
8
|
-
class DataPlugin extends Plugin {
|
|
9
|
-
beforeprerun () {
|
|
10
|
-
this.variables = []
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
prerun ({ tag, fragment, keys, errors, stack, pass }) {
|
|
14
|
-
if (pass !== 'renderer') return null
|
|
15
|
-
// TODO right data must be defined above usage
|
|
16
|
-
// consider changing interface of plugins
|
|
17
|
-
// to allow having 2 passes of walk instead of just one
|
|
18
|
-
// it could be declarative, e.g. new Plugin({ passes: 2 })
|
|
19
|
-
if (tag === 'data') {
|
|
20
|
-
if (keys.length > 0) {
|
|
21
|
-
const format = getDataFormat(keys)
|
|
22
|
-
const { content } = fragment.children[0]
|
|
23
|
-
const data = parseData(format, content)
|
|
24
|
-
if (isPlainObject(data)) {
|
|
25
|
-
for (const key in data) {
|
|
26
|
-
if (Object.prototype.hasOwnProperty.call(data, key)) {
|
|
27
|
-
const value = data[key]
|
|
28
|
-
this.variables.push({ key, value })
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
} else {
|
|
33
|
-
errors.push(createTranslationError('Data tag must specify a format (js, json or yaml).', stack))
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (CODE_TAGS.includes(tag)) {
|
|
38
|
-
fragment.children.forEach(node => {
|
|
39
|
-
node.skipDataOptimization = true
|
|
40
|
-
})
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (this.variables.length > 0 && !fragment.skipDataOptimization) {
|
|
44
|
-
optimizeNode(fragment, this.variables, [], true)
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
afterrun () {
|
|
49
|
-
this.variables = []
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
module.exports = DataPlugin
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
const { parse, walk, generate } = require('css-tree')
|
|
2
|
-
const { getExtension, getBase64Extension } = require('../../utilities/string')
|
|
3
|
-
const { findAsset, isFileSupported } = require('../../utilities/files')
|
|
4
|
-
|
|
5
|
-
function getBase64String (base64, path, options, isFont) {
|
|
6
|
-
const extension = getExtension(path)
|
|
7
|
-
const dataType = isFont ? 'data:application/font-' : 'data:image/'
|
|
8
|
-
return [
|
|
9
|
-
`${dataType}${getBase64Extension(extension)}`,
|
|
10
|
-
isFont && 'charset=utf-8',
|
|
11
|
-
`base64,${base64}`
|
|
12
|
-
].filter(Boolean).join(';')
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function convertElementValueToBase64 ({ element, value, assets, options, isFont }) {
|
|
16
|
-
if (!isFileSupported(value)) return
|
|
17
|
-
const asset = findAsset(value, assets, options)
|
|
18
|
-
if (!asset) return
|
|
19
|
-
element.value = getBase64String(asset.base64, asset.path, options, isFont)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function inlineUrls (tree, assets, options) {
|
|
23
|
-
walk(tree, node => {
|
|
24
|
-
if (node.type === 'Url') {
|
|
25
|
-
let { type, value } = node.value
|
|
26
|
-
value = value.replace(/'|"/g, '')
|
|
27
|
-
convertElementValueToBase64({ element: node.value, value, assets, options, isFont: type === 'Raw' })
|
|
28
|
-
}
|
|
29
|
-
})
|
|
30
|
-
return tree
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function cutStyles (tree) {
|
|
34
|
-
const styles = []
|
|
35
|
-
walk(tree, {
|
|
36
|
-
visit: 'Rule',
|
|
37
|
-
enter: node => {
|
|
38
|
-
walk(node.prelude, leaf => {
|
|
39
|
-
if (leaf.type === 'TypeSelector' || leaf.type === 'ClassSelector') {
|
|
40
|
-
const { name } = leaf
|
|
41
|
-
const block = generate(node.block)
|
|
42
|
-
if (name && block) {
|
|
43
|
-
const declaration = block
|
|
44
|
-
.replace(/^{/, '')
|
|
45
|
-
.replace(/}$/, '')
|
|
46
|
-
.replace(/"/g, "'")
|
|
47
|
-
styles.push({ type: leaf.type, name: leaf.name, declaration })
|
|
48
|
-
node.used = true
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
})
|
|
52
|
-
}
|
|
53
|
-
})
|
|
54
|
-
walk(tree, {
|
|
55
|
-
enter: (node, item, list) => {
|
|
56
|
-
if (item && item.data && item.data.used) {
|
|
57
|
-
list.remove(item)
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
})
|
|
61
|
-
return { styles, tree }
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function updateStyleAttribute (fragment, declaration) {
|
|
65
|
-
const styleAttribute = fragment.attributes.find(attribute => attribute.key === 'style')
|
|
66
|
-
if (styleAttribute) {
|
|
67
|
-
if (!styleAttribute.value.includes(declaration)) {
|
|
68
|
-
fragment.attributes = fragment.attributes.map(attribute => {
|
|
69
|
-
if (attribute.key === 'style') {
|
|
70
|
-
attribute.value += ';'.concat(declaration)
|
|
71
|
-
}
|
|
72
|
-
return attribute
|
|
73
|
-
})
|
|
74
|
-
}
|
|
75
|
-
} else {
|
|
76
|
-
fragment.attributes.push({ key: 'style', value: declaration })
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function applyStylesInFragment (fragment, styles) {
|
|
81
|
-
styles.filter(({ type }) => type === 'TypeSelector').forEach(style => {
|
|
82
|
-
if (style.name === fragment.tagName) {
|
|
83
|
-
const { declaration } = style
|
|
84
|
-
updateStyleAttribute(fragment, declaration)
|
|
85
|
-
}
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
const classAttribute = fragment.attributes.find(attribute => attribute.key === 'class')
|
|
89
|
-
if (classAttribute) {
|
|
90
|
-
const localStyles = classAttribute.value.split(/\s/).filter(Boolean) || []
|
|
91
|
-
fragment.attributes = fragment.attributes
|
|
92
|
-
.map(attribute => {
|
|
93
|
-
if (attribute.key === 'class') {
|
|
94
|
-
styles.filter(({ type }) => type === 'ClassSelector').forEach(({ name }) => {
|
|
95
|
-
attribute.value = attribute.value.replace(name, '')
|
|
96
|
-
attribute.value = attribute.value.replace(/\s+/, '')
|
|
97
|
-
return attribute.value ? attribute : null
|
|
98
|
-
})
|
|
99
|
-
}
|
|
100
|
-
return attribute
|
|
101
|
-
})
|
|
102
|
-
.filter(attribute => attribute.value)
|
|
103
|
-
styles
|
|
104
|
-
.filter(({ name, type }) => type === 'ClassSelector' && localStyles.includes(name))
|
|
105
|
-
.forEach(({ declaration }) => {
|
|
106
|
-
updateStyleAttribute(fragment, declaration)
|
|
107
|
-
})
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function prepareStyles (css, assets, options) {
|
|
112
|
-
let tree = parse(css)
|
|
113
|
-
tree = inlineUrls(tree, assets, options)
|
|
114
|
-
const result = cutStyles(tree)
|
|
115
|
-
return { styles: result.styles, output: generate(result.tree) }
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function removeEmptyStyleTag (fragment) {
|
|
119
|
-
if (fragment.tagName === 'style' &&
|
|
120
|
-
fragment.attributes.find(({ key }) => key === 'inline') &&
|
|
121
|
-
fragment.children &&
|
|
122
|
-
fragment.children.length === 1 &&
|
|
123
|
-
fragment.children[0].content === ''
|
|
124
|
-
) {
|
|
125
|
-
fragment.type = 'text'
|
|
126
|
-
fragment.content = ''
|
|
127
|
-
fragment.children = []
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
module.exports = { prepareStyles, applyStylesInFragment, removeEmptyStyleTag, convertElementValueToBase64 }
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const Plugin = require('../Plugin')
|
|
4
|
-
const { convertElementValueToBase64, prepareStyles, applyStylesInFragment, removeEmptyStyleTag } = require('./css')
|
|
5
|
-
|
|
6
|
-
class InlinePlugin extends Plugin {
|
|
7
|
-
constructor () {
|
|
8
|
-
super()
|
|
9
|
-
this.styles = []
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
beforeprerun () {
|
|
13
|
-
this.styles = []
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
prerun ({ fragment, keys, assets, options }) {
|
|
17
|
-
if (fragment.tagName === 'style' && keys.includes('inline')) {
|
|
18
|
-
const child = fragment.children[0]
|
|
19
|
-
const { styles, output } = prepareStyles(child.content, assets, options)
|
|
20
|
-
styles.forEach(style => this.styles.push(style))
|
|
21
|
-
child.content = output
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
run ({ fragment, keys, assets, options }) {
|
|
26
|
-
if (fragment.tagName === 'font' && keys.includes('inline')) {
|
|
27
|
-
const attribute = fragment.attributes.find(attribute => attribute.key === 'from')
|
|
28
|
-
convertElementValueToBase64({ element: attribute, value: attribute.value, assets, options, isFont: true })
|
|
29
|
-
}
|
|
30
|
-
if (fragment.type === 'element' && fragment.tagName !== 'style' && this.styles.length) {
|
|
31
|
-
applyStylesInFragment(fragment, this.styles)
|
|
32
|
-
}
|
|
33
|
-
if (fragment.tagName === 'style') {
|
|
34
|
-
removeEmptyStyleTag(fragment)
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
module.exports = InlinePlugin
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const Plugin = require('../Plugin')
|
|
4
|
-
const { findAsset } = require('../../utilities/files')
|
|
5
|
-
const hash = require('string-hash')
|
|
6
|
-
const { parseData, getDataFormat } = require('../../utilities/data')
|
|
7
|
-
const { createTranslationError } = require('../../utilities/errors')
|
|
8
|
-
|
|
9
|
-
function validateTranslations (value, translations, languages, errors, stack) {
|
|
10
|
-
if (value.includes('{') || value.includes('}')) return translations
|
|
11
|
-
if (!translations[value]) {
|
|
12
|
-
errors.push(createTranslationError(`There is no translation for the ${value} key`, stack))
|
|
13
|
-
return translations
|
|
14
|
-
}
|
|
15
|
-
languages.forEach((language, index) => {
|
|
16
|
-
if (!translations[value][index] && !translations[value][language]) errors.push(createTranslationError(`There is no translation for the ${value} key in ${language} language.`, stack))
|
|
17
|
-
})
|
|
18
|
-
return translations
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function getExtension (path) {
|
|
22
|
-
const values = path.split('.')
|
|
23
|
-
return values[values.length - 1]
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function isI18nTag (tag, keys) {
|
|
27
|
-
return (tag === 'script' && keys.includes('i18n')) || tag === 'i18n'
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function isDataTag (tag) {
|
|
31
|
-
return tag === 'data'
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function hasTranslateModifier (attribute) {
|
|
35
|
-
return attribute.key.endsWith('|translate')
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function getTranslationKey (key) {
|
|
39
|
-
if (key.includes('|')) {
|
|
40
|
-
return `${key.split('|')[0]}`
|
|
41
|
-
}
|
|
42
|
-
return key
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
class InternationalizationPlugin extends Plugin {
|
|
46
|
-
constructor ({ translations, filters, errors }) {
|
|
47
|
-
super()
|
|
48
|
-
this.translations = translations
|
|
49
|
-
this.filters = filters
|
|
50
|
-
this.errors = errors
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
prerun ({ source, tag, attrs, keys, fragment, assets, stack, options }) {
|
|
54
|
-
const id = `__scope_${hash(source)}`
|
|
55
|
-
if (isI18nTag(tag, keys)) {
|
|
56
|
-
fragment.used = true
|
|
57
|
-
let leaf = fragment.children[0]
|
|
58
|
-
if (keys.includes('from')) {
|
|
59
|
-
const { value: path } = attrs.find(attr => attr.key === 'from')
|
|
60
|
-
const asset = findAsset(path, assets, options)
|
|
61
|
-
if (!asset) return
|
|
62
|
-
leaf = { content: asset.source }
|
|
63
|
-
leaf.used = true
|
|
64
|
-
keys.push(getExtension(path))
|
|
65
|
-
} else if (!leaf || !leaf.content || !leaf.content.trim()) {
|
|
66
|
-
return this.errors.push(createTranslationError('The translation script cannot be empty', stack))
|
|
67
|
-
}
|
|
68
|
-
leaf.used = true
|
|
69
|
-
const format = getDataFormat(keys)
|
|
70
|
-
const data = parseData(format, leaf.content)
|
|
71
|
-
for (const key in data) {
|
|
72
|
-
if (this.translations[key]) { return this.errors.push(createTranslationError('Translation already exists', stack)) }
|
|
73
|
-
this.translations[`${key}_${id}`] = data[key]
|
|
74
|
-
}
|
|
75
|
-
} else if (isDataTag(tag)) {
|
|
76
|
-
const format = getDataFormat(keys)
|
|
77
|
-
const leaf = fragment.children[0]
|
|
78
|
-
const data = parseData(format, leaf.content)
|
|
79
|
-
const { i18n } = data
|
|
80
|
-
for (const key in i18n) {
|
|
81
|
-
if (this.translations[key]) { return this.errors.push(createTranslationError('Translation already exists', stack)) }
|
|
82
|
-
this.translations[`${key}_${id}`] = i18n[key]
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
attrs.forEach(attr => {
|
|
86
|
-
if (hasTranslateModifier(attr)) {
|
|
87
|
-
attr.key = attr.key.substring(0, attr.key.indexOf('|translate'))
|
|
88
|
-
attr.value = `{"${attr.value}_${id}"|translate(language)}`
|
|
89
|
-
}
|
|
90
|
-
})
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
run ({ source, tag, attrs, options, stack, fragment }) {
|
|
94
|
-
const id = `__scope_${hash(source)}`
|
|
95
|
-
if (tag === 'translate' && !fragment.scopedTranslations) {
|
|
96
|
-
fragment.scopedTranslations = true
|
|
97
|
-
const { languages } = options
|
|
98
|
-
const attribute = attrs[0]
|
|
99
|
-
if (attribute) {
|
|
100
|
-
const { key } = attribute
|
|
101
|
-
this.filters.push('translate')
|
|
102
|
-
this.translations = validateTranslations(`${getTranslationKey(key)}_${id}`, this.translations, languages, this.errors, stack)
|
|
103
|
-
attribute.key = `${key}_${id}`
|
|
104
|
-
} else {
|
|
105
|
-
this.errors.push(createTranslationError('Translate tag must define a key', stack))
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
module.exports = InternationalizationPlugin
|
package/src/plugins/Plugin.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
class Plugin {
|
|
4
|
-
constructor () {
|
|
5
|
-
this.depth = 0
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
prerun (options) {}
|
|
9
|
-
run (options) {}
|
|
10
|
-
postrun (options) {}
|
|
11
|
-
|
|
12
|
-
beforeprerun (options) {}
|
|
13
|
-
afterprerun (options) {}
|
|
14
|
-
|
|
15
|
-
beforerun (options) {}
|
|
16
|
-
afterrun (options) {}
|
|
17
|
-
|
|
18
|
-
beforepostrun (options) {}
|
|
19
|
-
afterpostrun (options) {}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
module.exports = Plugin
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const { parse, walk, generate } = require('css-tree')
|
|
4
|
-
const { hash } = require('../../utilities/string')
|
|
5
|
-
const { normalizeNewline } = require('../../utilities/string')
|
|
6
|
-
|
|
7
|
-
function addScopeToCssSelectors (input, scopes) {
|
|
8
|
-
const content = normalizeNewline(input).trim()
|
|
9
|
-
const id = hash(content)
|
|
10
|
-
const tree = parse(content)
|
|
11
|
-
const keyframes = {}
|
|
12
|
-
walk(tree, node => {
|
|
13
|
-
if (node.type === 'Atrule' && node.name === 'keyframes') {
|
|
14
|
-
const child = node.prelude.children.first()
|
|
15
|
-
const marker = `${child.name}-${id}`
|
|
16
|
-
keyframes[child.name] = marker
|
|
17
|
-
child.name = marker
|
|
18
|
-
}
|
|
19
|
-
if (node.type === 'SelectorList') {
|
|
20
|
-
node.children.forEach(child => {
|
|
21
|
-
if (child.type === 'Selector') {
|
|
22
|
-
let hasScope = false
|
|
23
|
-
child.children.forEach(leaf => {
|
|
24
|
-
if (leaf.type === 'ClassSelector' && !hasScope) {
|
|
25
|
-
scopes.push({ type: 'class', name: leaf.name, id })
|
|
26
|
-
hasScope = true
|
|
27
|
-
} else if (leaf.type === 'TypeSelector' && !hasScope) {
|
|
28
|
-
scopes.push({ type: 'tag', name: leaf.name, id })
|
|
29
|
-
hasScope = true
|
|
30
|
-
}
|
|
31
|
-
})
|
|
32
|
-
if (hasScope) {
|
|
33
|
-
const node = child.children.first()
|
|
34
|
-
if (node.type === 'TypeSelector') { child.children.shift() }
|
|
35
|
-
child.children.unshift({ type: 'ClassSelector', loc: null, name: id })
|
|
36
|
-
if (node.type === 'TypeSelector') { child.children.unshift(node) }
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
})
|
|
40
|
-
}
|
|
41
|
-
})
|
|
42
|
-
function isAnimationProperty (property) {
|
|
43
|
-
return property === 'animation' || property === 'animation-name'
|
|
44
|
-
}
|
|
45
|
-
walk(tree, node => {
|
|
46
|
-
if (node.type === 'Declaration' && isAnimationProperty(node.property)) {
|
|
47
|
-
const child = node.value.children.first()
|
|
48
|
-
if (keyframes[child.name]) {
|
|
49
|
-
child.name = keyframes[child.name]
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
})
|
|
53
|
-
return generate(tree)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
module.exports = { addScopeToCssSelectors }
|