@cssxjs/loaders 0.2.7
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/CHANGELOG.md +9 -0
- package/callLoader.js +22 -0
- package/cssToReactNativeLoader.js +86 -0
- package/package.json +26 -0
- package/patches/patchStylusAddUnit.js +43 -0
- package/stylusToCssLoader.js +70 -0
package/CHANGELOG.md
ADDED
package/callLoader.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Simple mock to be able to call simple webpack loaders with filename substitution.
|
|
2
|
+
module.exports = function callLoader (loader, source, filename, options = {}) {
|
|
3
|
+
let isAsync = false
|
|
4
|
+
let resolveAsync
|
|
5
|
+
let rejectAsync
|
|
6
|
+
const markAsync = () => {
|
|
7
|
+
isAsync = true
|
|
8
|
+
return (err, result) => {
|
|
9
|
+
if (err) return rejectAsync(err)
|
|
10
|
+
resolveAsync(result)
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
const result = loader.call({ resourcePath: filename, query: options, async: markAsync }, source)
|
|
14
|
+
if (isAsync) {
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
resolveAsync = resolve
|
|
17
|
+
rejectAsync = reject
|
|
18
|
+
})
|
|
19
|
+
} else {
|
|
20
|
+
return result
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// ref: https://github.com/kristerkari/react-native-css-transformer
|
|
2
|
+
const css2rn = require('@startupjs/css-to-react-native-transform').default
|
|
3
|
+
|
|
4
|
+
const EXPORT_REGEX = /:export\s*\{/
|
|
5
|
+
|
|
6
|
+
module.exports = function cssToReactNative (source) {
|
|
7
|
+
source = escapeExport(source)
|
|
8
|
+
const cssObject = css2rn(source, { parseMediaQueries: true, parsePartSelectors: true })
|
|
9
|
+
for (const key in cssObject.__exportProps || {}) {
|
|
10
|
+
cssObject[key] = parseStylValue(cssObject.__exportProps[key])
|
|
11
|
+
}
|
|
12
|
+
// save hash to use with the caching system of @startupjs/cache
|
|
13
|
+
cssObject.__hash__ = simpleNumericHash(JSON.stringify(cssObject))
|
|
14
|
+
return 'module.exports = ' + JSON.stringify(cssObject)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function parseStylValue (value) {
|
|
18
|
+
if (typeof value !== 'string') return value
|
|
19
|
+
// strip single quotes (stylus adds it for the topmost value)
|
|
20
|
+
// and parens (stylus adds them for values in a hash)
|
|
21
|
+
// Instead of doing a simple regex replace for both beginning and end,
|
|
22
|
+
// we only find beginning chars and then cut string from both sides.
|
|
23
|
+
// This is needed to prevent false-replacing the paren at the end of
|
|
24
|
+
// values like 'rgba(...)'
|
|
25
|
+
if (/^['"(]/.test(value)) {
|
|
26
|
+
const wrapsLength = value.match(/^['"(]+/)[0].length
|
|
27
|
+
value = value.slice(wrapsLength).slice(0, -wrapsLength)
|
|
28
|
+
}
|
|
29
|
+
// hash
|
|
30
|
+
if (value.charAt(0) === '{') {
|
|
31
|
+
const hash = JSON.parse(value)
|
|
32
|
+
for (const key in hash) {
|
|
33
|
+
hash[key] = parseStylValue(hash[key])
|
|
34
|
+
}
|
|
35
|
+
return hash
|
|
36
|
+
} else if (!isNaN(parseFloat(value))) {
|
|
37
|
+
return parseFloat(value)
|
|
38
|
+
} else {
|
|
39
|
+
return value
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Process :export properties by wrapping their values in quotes
|
|
44
|
+
function escapeExport (source) {
|
|
45
|
+
const match = source.match(EXPORT_REGEX)
|
|
46
|
+
if (!match) return source
|
|
47
|
+
|
|
48
|
+
// 1. find closing bracket of :export { ... }
|
|
49
|
+
const matchIndex = match.index
|
|
50
|
+
const matchStr = match[0]
|
|
51
|
+
const matchLength = matchStr.length
|
|
52
|
+
const start = matchIndex + matchLength
|
|
53
|
+
let openBr = 1 // Count opened brackets, we start from one already opened
|
|
54
|
+
let end
|
|
55
|
+
|
|
56
|
+
for (let i = start; i < source.length; i++) {
|
|
57
|
+
if (source.charAt(i) === '}') {
|
|
58
|
+
--openBr
|
|
59
|
+
} else if (source.charAt(i) === '{') {
|
|
60
|
+
++openBr
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (openBr <= 0) {
|
|
64
|
+
end = i
|
|
65
|
+
break
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (!end) return source
|
|
69
|
+
|
|
70
|
+
// 2. escape all exported values
|
|
71
|
+
const properties = source
|
|
72
|
+
.slice(start, end)
|
|
73
|
+
.split(';')
|
|
74
|
+
.map(line => line.replace(/(:\s+)([^'"].*[^'"])$/, '$1\'$2\''))
|
|
75
|
+
.join(';')
|
|
76
|
+
|
|
77
|
+
source = source.slice(0, start) + properties + source.slice(end)
|
|
78
|
+
|
|
79
|
+
return source
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ref: https://gist.github.com/hyamamoto/fd435505d29ebfa3d9716fd2be8d42f0?permalink_comment_id=2694461#gistcomment-2694461
|
|
83
|
+
function simpleNumericHash (s) {
|
|
84
|
+
for (var i = 0, h = 0; i < s.length; i++) h = Math.imul(31, h) + s.charCodeAt(i) | 0
|
|
85
|
+
return h
|
|
86
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cssxjs/loaders",
|
|
3
|
+
"version": "0.2.7",
|
|
4
|
+
"description": "Webpack-compatible loaders for CSSX styles in React Native and Web bundlers",
|
|
5
|
+
"exports": {
|
|
6
|
+
"./callLoader": "./callLoader.js",
|
|
7
|
+
"./stylusToCssLoader": "./stylusToCssLoader.js",
|
|
8
|
+
"./cssToReactNativeLoader": "./cssToReactNativeLoader.js"
|
|
9
|
+
},
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"test": "echo 'No tests yet' && exit 0"
|
|
15
|
+
},
|
|
16
|
+
"author": {
|
|
17
|
+
"name": "Pavel Zhukov",
|
|
18
|
+
"email": "cray0000@gmail.com"
|
|
19
|
+
},
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@startupjs/css-to-react-native-transform": "^2.1.0-1",
|
|
23
|
+
"stylus": "0.64.0"
|
|
24
|
+
},
|
|
25
|
+
"gitHead": "df9297432a485b18a13127558a62df7be263cac2"
|
|
26
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// add a new custom unit 'u' which is equal to 8px
|
|
2
|
+
const units = require('stylus/lib/units.js')
|
|
3
|
+
const Unit = require('stylus/lib/nodes/unit.js')
|
|
4
|
+
let patched = false
|
|
5
|
+
|
|
6
|
+
module.exports = function patchStylusAddUnit () {
|
|
7
|
+
if (patched) return
|
|
8
|
+
patched = true
|
|
9
|
+
|
|
10
|
+
// add the unit itself
|
|
11
|
+
if (!units.includes('u')) units.push('u')
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* a monkey patch to convert the 'type' to be dynamic setter/getter
|
|
15
|
+
* with the real value stored in a private symbol. This simulates having 'u' unit converted to 'px' * 8 on assignment.
|
|
16
|
+
*
|
|
17
|
+
* Original constructor is the following:
|
|
18
|
+
*
|
|
19
|
+
* constructor(val, type) {
|
|
20
|
+
* super();
|
|
21
|
+
* this.val = val;
|
|
22
|
+
* this.type = type;
|
|
23
|
+
* }
|
|
24
|
+
*
|
|
25
|
+
* this.val and this.type are never reassigned.
|
|
26
|
+
* This make it possible to hack the this.type assignment specifically by auto-multiplying the this.val by 8
|
|
27
|
+
* during the attempt to assign the 'u' to this.type
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
const TYPE = Symbol('unit.type')
|
|
31
|
+
Object.defineProperty(Unit.prototype, 'type', {
|
|
32
|
+
configurable: true,
|
|
33
|
+
enumerable: true,
|
|
34
|
+
get () { return this[TYPE] },
|
|
35
|
+
set (t) {
|
|
36
|
+
if (t === 'u') {
|
|
37
|
+
t = 'px'
|
|
38
|
+
if (typeof this.val === 'number') this.val = this.val * 8
|
|
39
|
+
}
|
|
40
|
+
this[TYPE] = t
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// ref: https://github.com/kristerkari/react-native-stylus-transformer
|
|
2
|
+
const { existsSync } = require('fs')
|
|
3
|
+
const { join } = require('path')
|
|
4
|
+
const stylus = require('stylus')
|
|
5
|
+
const patchStylusAddUnit = require('./patches/patchStylusAddUnit.js')
|
|
6
|
+
|
|
7
|
+
// apply patches
|
|
8
|
+
patchStylusAddUnit()
|
|
9
|
+
|
|
10
|
+
const PROJECT_STYLES_PATH = join(process.cwd(), 'styles/index.styl')
|
|
11
|
+
let UI_STYLES_PATH
|
|
12
|
+
|
|
13
|
+
function renderToCSS (src, filename, { platform } = {}) {
|
|
14
|
+
const compiler = stylus(src)
|
|
15
|
+
compiler.set('filename', filename)
|
|
16
|
+
|
|
17
|
+
if (platform) {
|
|
18
|
+
compiler.define('$PLATFORM', platform)
|
|
19
|
+
compiler.define(`__${platform.toUpperCase()}__`, true)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (checkUiStylesExist()) {
|
|
23
|
+
compiler.import(UI_STYLES_PATH)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// TODO: Make this a setting
|
|
27
|
+
if (checkProjectStylesExist()) {
|
|
28
|
+
compiler.import(PROJECT_STYLES_PATH)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let compiled
|
|
32
|
+
compiler.render(function (err, res) {
|
|
33
|
+
if (err) {
|
|
34
|
+
throw new Error(err)
|
|
35
|
+
}
|
|
36
|
+
compiled = res
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
return compiled
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = function stylusToReactNative (source) {
|
|
43
|
+
return renderToCSS(source, this.resourcePath, this.query)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// check if @startupjs/ui is being used to load styles file from it, cache result for 5 seconds
|
|
47
|
+
let uiStylesExist
|
|
48
|
+
let uiStylesLastChecked = 0
|
|
49
|
+
function checkUiStylesExist () {
|
|
50
|
+
if (uiStylesLastChecked + 5000 > Date.now()) return uiStylesExist
|
|
51
|
+
uiStylesLastChecked = Date.now()
|
|
52
|
+
try {
|
|
53
|
+
// TODO: make this configurable
|
|
54
|
+
UI_STYLES_PATH = join(require.resolve('@startupjs/ui'), '../styles/index.styl')
|
|
55
|
+
uiStylesExist = existsSync(UI_STYLES_PATH)
|
|
56
|
+
} catch {
|
|
57
|
+
uiStylesExist = false
|
|
58
|
+
}
|
|
59
|
+
return uiStylesExist
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// check if project styles file exist, cache result for 5 seconds
|
|
63
|
+
let projectStylesExist
|
|
64
|
+
let projectStylesLastChecked = 0
|
|
65
|
+
function checkProjectStylesExist () {
|
|
66
|
+
if (projectStylesLastChecked + 5000 > Date.now()) return projectStylesExist
|
|
67
|
+
projectStylesLastChecked = Date.now()
|
|
68
|
+
projectStylesExist = existsSync(PROJECT_STYLES_PATH)
|
|
69
|
+
return projectStylesExist
|
|
70
|
+
}
|