@zohodesk/react-cli 0.0.1-exp.168.1 → 0.0.1-exp.169.1
Sign up to get free protection for your applications and to get access to all the features.
- package/.prettierrc +1 -0
- package/README.md +26 -0
- package/docs/HoverActive.md +12 -0
- package/lib/configs/jest.config.js +1 -3
- package/lib/configs/webpack.dev.config.js +5 -3
- package/lib/configs/webpack.docs.config.js +4 -2
- package/lib/configs/webpack.impact.config.js +4 -2
- package/lib/configs/webpack.prod.config.js +7 -3
- package/lib/jest/preProcessors/cssPreprocessor.js +2 -0
- package/lib/loaderUtils/configsAssetsLoaders.js +29 -0
- package/lib/loaderUtils/getCSSLoaders.js +5 -3
- package/lib/plugins/I18nSplitPlugin/I18nSplit.md +63 -54
- package/lib/postcss-plugins/__test__/hoverActivePlugin.spec.js +22 -0
- package/lib/postcss-plugins/__test__/test1Input.css +39 -0
- package/lib/postcss-plugins/__test__/test1Output.css +39 -0
- package/lib/postcss-plugins/hoverActivePlugin.js +365 -0
- package/lib/schemas/index.js +8 -1
- package/lib/servers/getCliPath.js +6 -8
- package/package.json +4 -1
package/.prettierrc
CHANGED
package/README.md
CHANGED
@@ -2,6 +2,32 @@
|
|
2
2
|
|
3
3
|
A CLI tool for build modern web application and libraries
|
4
4
|
|
5
|
+
# 0.0.1-exp.169.1
|
6
|
+
|
7
|
+
File support added for mp4 for docs dev and prod mode
|
8
|
+
|
9
|
+
# 0.0.1-beta.169
|
10
|
+
|
11
|
+
this version has same as `0.0.1-exp.168.1`, `0.0.1-exp.168.2`, `0.0.1-exp.168.3` and below and some minor changes
|
12
|
+
|
13
|
+
1. `--efc_version=v3` flag added for change efc version via commmand line option
|
14
|
+
2. `hoverActive` flag added for conversion from usual hover to @media(hover:hover) and @media(hover:none) queries for hover support in mobile.
|
15
|
+
3. `combinerMq` flag added for combination of the media queries that might be appended or existing media queries that are duplicates to create one media query with all rules put together.
|
16
|
+
|
17
|
+
# 0.0.1-exp.168.3
|
18
|
+
|
19
|
+
Changes :
|
20
|
+
|
21
|
+
1. code optimization ( made functions for the hoverActive cases making it more easier to access)
|
22
|
+
2. handled `hover:ignore`, `active:ignore`, `hoverActive:ignore` cases for usual queries and media queries
|
23
|
+
|
24
|
+
# 0.0.1-exp.168.2
|
25
|
+
|
26
|
+
Changes :
|
27
|
+
|
28
|
+
1. hoverActive case handled with postcss `hover:hover` media query and `hover:none` media query is added
|
29
|
+
2. flag for hoverActive used to add/remove postcss changes
|
30
|
+
|
5
31
|
# 0.0.1-exp.168.1
|
6
32
|
|
7
33
|
Changes:-
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# hover active css handling
|
2
|
+
|
3
|
+
In general we write css with some hover in it.
|
4
|
+
But In mobile there is no hover and some times this hover css may do something messy some thing like we may need to click a button we need to click it double time.
|
5
|
+
|
6
|
+
To handle this behaviour we write a postcss plugin
|
7
|
+
|
8
|
+
PostCSS plugin that transforms :hover selectors to :active on devices where a mouse isn't the primary input mechanism.
|
9
|
+
|
10
|
+
when we write hover css it will have some problem in mobile devices the reason was in mobile device ther is no mouse
|
11
|
+
|
12
|
+
for this we have to handle that as
|
@@ -27,9 +27,7 @@ let commonConfig = {
|
|
27
27
|
'^.+\\.css$': _path.default.resolve(__dirname, '..', 'jest', 'preProcessors', 'cssPreprocessor.js'),
|
28
28
|
'^(?!.*\\.(js|jsx|css|json)$)': _path.default.resolve(__dirname, '..', 'jest', 'preProcessors', 'otherFilesPreprocessor.js')
|
29
29
|
},
|
30
|
-
moduleNameMapper:
|
31
|
-
'\\.(css|less)$': 'identity-obj-proxy'
|
32
|
-
},
|
30
|
+
moduleNameMapper: moduleNameMapper,
|
33
31
|
transformIgnorePatterns: ['/node_modules/(?!(@zohodesk)/)'],
|
34
32
|
// transformIgnorePatterns: ['/node_modules.*?.js$'],
|
35
33
|
moduleFileExtensions: ['js'],
|
@@ -25,7 +25,9 @@ let {
|
|
25
25
|
server,
|
26
26
|
outputFolder,
|
27
27
|
rtlExclude,
|
28
|
+
combinerMq,
|
28
29
|
hasRTL,
|
30
|
+
hoverActive,
|
29
31
|
cssUniqueness,
|
30
32
|
seperateCssModules,
|
31
33
|
changeRuntimeChunkChar,
|
@@ -98,11 +100,11 @@ module.exports = {
|
|
98
100
|
}, seperateCssModules ? {
|
99
101
|
test: /\.css$/,
|
100
102
|
exclude: /\.module\.css$/,
|
101
|
-
use: (0, _loaderUtils.getCSSLoaders)(hasRTL, rtlExclude, '[local]', false, null)
|
103
|
+
use: (0, _loaderUtils.getCSSLoaders)(hasRTL, rtlExclude, combinerMq, hoverActive, '[local]', false, null)
|
102
104
|
} : null, {
|
103
105
|
test: seperateCssModules ? /\.module\.css$/ : /(\.module)?\.css$/,
|
104
|
-
use: (0, _loaderUtils.getCSSLoaders)(hasRTL, rtlExclude, null, cssUniqueness, selectorReplace, cssHashSelectors, classNamePrefix)
|
105
|
-
}, (0, _configsAssetsLoaders.configImageLoader)(nameTemplate), (0, _configsAssetsLoaders.configFontLoader)(nameTemplate), (0, _configsAssetsLoaders.configSVGLoader)(nameTemplate), (0, _configsAssetsLoaders.configAudioLoader)(nameTemplate), {
|
106
|
+
use: (0, _loaderUtils.getCSSLoaders)(hasRTL, combinerMq, rtlExclude, hoverActive, null, cssUniqueness, selectorReplace, cssHashSelectors, classNamePrefix)
|
107
|
+
}, (0, _configsAssetsLoaders.configImageLoader)(nameTemplate), (0, _configsAssetsLoaders.configFontLoader)(nameTemplate), (0, _configsAssetsLoaders.configSVGLoader)(nameTemplate), (0, _configsAssetsLoaders.configVideoLoader)(nameTemplate), (0, _configsAssetsLoaders.configAudioLoader)(nameTemplate), {
|
106
108
|
test: /\.tmpl$/,
|
107
109
|
use: [{
|
108
110
|
loader: 'html-loader',
|
@@ -22,7 +22,9 @@ let {
|
|
22
22
|
disableES5Transpile,
|
23
23
|
enableChunkHash,
|
24
24
|
cssUniqueness,
|
25
|
+
combinerMq,
|
25
26
|
hasRTL,
|
27
|
+
hoverActive,
|
26
28
|
rtlExclude,
|
27
29
|
cssHashSelectors,
|
28
30
|
classNamePrefix
|
@@ -73,8 +75,8 @@ module.exports = isSSTest => ({
|
|
73
75
|
exclude: /node_modules/
|
74
76
|
}, {
|
75
77
|
test: /(\.module)?\.css$/,
|
76
|
-
use: (0, _loaderUtils.getCSSLoaders)(hasRTL, rtlExclude, false, cssUniqueness, null, cssHashSelectors, classNamePrefix)
|
77
|
-
}, (0, _configsAssetsLoaders.configImageLoader)(nameTemplate), (0, _configsAssetsLoaders.configFontLoader)(nameTemplate), (0, _configsAssetsLoaders.configSVGLoader)(nameTemplate), (0, _configsAssetsLoaders.configAudioLoader)(nameTemplate), {
|
78
|
+
use: (0, _loaderUtils.getCSSLoaders)(hasRTL, combinerMq, rtlExclude, hoverActive, false, cssUniqueness, null, cssHashSelectors, classNamePrefix)
|
79
|
+
}, (0, _configsAssetsLoaders.configImageLoader)(nameTemplate), (0, _configsAssetsLoaders.configFontLoader)(nameTemplate), (0, _configsAssetsLoaders.configSVGLoader)(nameTemplate), (0, _configsAssetsLoaders.configAudioLoader)(nameTemplate), (0, _configsAssetsLoaders.configVideoLoader)(nameTemplate), {
|
78
80
|
test: /\.html$/,
|
79
81
|
use: {
|
80
82
|
loader: 'html-loader',
|
@@ -19,6 +19,8 @@ let {
|
|
19
19
|
cssUniqueness,
|
20
20
|
hasRTL,
|
21
21
|
rtlExclude,
|
22
|
+
hoverActive,
|
23
|
+
combinerMq,
|
22
24
|
cssHashSelectors,
|
23
25
|
enableChunkHash,
|
24
26
|
classNamePrefix
|
@@ -68,8 +70,8 @@ module.exports = {
|
|
68
70
|
exclude: /node_modules/
|
69
71
|
}, {
|
70
72
|
test: /(\.module)?\.css$/,
|
71
|
-
use: (0, _loaderUtils.getCSSLoaders)(hasRTL, rtlExclude, false, cssUniqueness, null, cssHashSelectors, classNamePrefix)
|
72
|
-
}, (0, _configsAssetsLoaders.configImageLoader)(nameTemplate), (0, _configsAssetsLoaders.configFontLoader)(nameTemplate), (0, _configsAssetsLoaders.configSVGLoader)(nameTemplate), (0, _configsAssetsLoaders.configAudioLoader)(nameTemplate), {
|
73
|
+
use: (0, _loaderUtils.getCSSLoaders)(hasRTL, rtlExclude, combinerMq, hoverActive, false, cssUniqueness, null, cssHashSelectors, classNamePrefix)
|
74
|
+
}, (0, _configsAssetsLoaders.configImageLoader)(nameTemplate), (0, _configsAssetsLoaders.configFontLoader)(nameTemplate), (0, _configsAssetsLoaders.configSVGLoader)(nameTemplate), (0, _configsAssetsLoaders.configAudioLoader)(nameTemplate), (0, _configsAssetsLoaders.configVideoLoader)(nameTemplate), {
|
73
75
|
test: /\.html$/,
|
74
76
|
use: {
|
75
77
|
loader: 'html-loader',
|
@@ -12,6 +12,8 @@ var _loaderUtils = require("../loaderUtils");
|
|
12
12
|
|
13
13
|
var _libAlias = require("./libAlias");
|
14
14
|
|
15
|
+
var _configsAssetsLoaders = require("../loaderUtils/configsAssetsLoaders");
|
16
|
+
|
15
17
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
16
18
|
|
17
19
|
// import TerserPlugin from 'terser-webpack-plugin';
|
@@ -27,8 +29,10 @@ let {
|
|
27
29
|
removeAttribute,
|
28
30
|
enableSMap,
|
29
31
|
server,
|
32
|
+
combinerMq,
|
30
33
|
hasRTL,
|
31
34
|
rtlExclude,
|
35
|
+
hoverActive,
|
32
36
|
cssUniqueness,
|
33
37
|
server: {
|
34
38
|
mode
|
@@ -144,10 +148,10 @@ module.exports = {
|
|
144
148
|
}, seperateCssModules ? {
|
145
149
|
test: /\.css$/,
|
146
150
|
exclude: /\.module\.css$/,
|
147
|
-
use: (0, _loaderUtils.getCSSLoaders)(hasRTL, rtlExclude, '[local]', false, null)
|
151
|
+
use: (0, _loaderUtils.getCSSLoaders)(hasRTL, rtlExclude, hoverActive, combinerMq, '[local]', false, null)
|
148
152
|
} : null, {
|
149
153
|
test: seperateCssModules ? /\.module\.css$/ : /\.css$/,
|
150
|
-
use: (0, _loaderUtils.getCSSLoaders)(hasRTL, rtlExclude, false, cssUniqueness, selectorReplace, cssHashSelectors, classNamePrefix)
|
154
|
+
use: (0, _loaderUtils.getCSSLoaders)(hasRTL, combinerMq, rtlExclude, hoverActive, false, cssUniqueness, selectorReplace, cssHashSelectors, classNamePrefix)
|
151
155
|
}, {
|
152
156
|
test: /\.jpe?g$|\.gif$|\.png$/,
|
153
157
|
use: [{
|
@@ -158,7 +162,7 @@ module.exports = {
|
|
158
162
|
fallback: _path.default.join(__dirname, '..', 'loaders', 'fileLoader.js')
|
159
163
|
}
|
160
164
|
}]
|
161
|
-
}, {
|
165
|
+
}, (0, _configsAssetsLoaders.configVideoLoaderObj)(enableChunkHash ? './images/[name].[hash:20].[ext]' : './images/[name].[ext]'), {
|
162
166
|
test: /\.woff2|\.woff$|\.ttf$|\.eot$/,
|
163
167
|
use: [{
|
164
168
|
loader: 'url-loader',
|
@@ -7,12 +7,20 @@ exports.configAudioLoader = configAudioLoader;
|
|
7
7
|
exports.configFontLoader = configFontLoader;
|
8
8
|
exports.configImageLoader = configImageLoader;
|
9
9
|
exports.configSVGLoader = configSVGLoader;
|
10
|
+
exports.configVideoLoader = configVideoLoader;
|
11
|
+
exports.configVideoLoaderObj = configVideoLoaderObj;
|
10
12
|
exports.createNameTemplate = createNameTemplate;
|
13
|
+
|
14
|
+
var _path = _interopRequireDefault(require("path"));
|
15
|
+
|
16
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
17
|
+
|
11
18
|
// function getLoaderOptionQueryString(params) {
|
12
19
|
const ImageExtRegex = /\.jpe?g$|\.gif$|\.png$/;
|
13
20
|
const FontExtRegex = /\.woff2|\.woff$|\.ttf$|\.eot$/;
|
14
21
|
const SVGExtRegex = /\.svg$/;
|
15
22
|
const AudioExtRegex = /\.ogg$/;
|
23
|
+
const VidioExtRegex = /\.mp4$/;
|
16
24
|
|
17
25
|
function createLoaderOptionQueryString(loaderName, nameTemplate, limit = 1000) {
|
18
26
|
return `${loaderName}?limit=${limit}&name=${nameTemplate}`;
|
@@ -46,6 +54,27 @@ function configAudioLoader(nameTemplate) {
|
|
46
54
|
};
|
47
55
|
}
|
48
56
|
|
57
|
+
function configVideoLoader(nameTemplate) {
|
58
|
+
return {
|
59
|
+
test: VidioExtRegex,
|
60
|
+
use: createLoaderOptionQueryString('url-loader', `./images/${nameTemplate}`, 1)
|
61
|
+
};
|
62
|
+
}
|
63
|
+
|
64
|
+
function configVideoLoaderObj(nameTemplate) {
|
65
|
+
return {
|
66
|
+
test: VidioExtRegex,
|
67
|
+
use: {
|
68
|
+
loader: 'url-loader',
|
69
|
+
options: {
|
70
|
+
limit: 1000,
|
71
|
+
name: nameTemplate,
|
72
|
+
fallback: _path.default.join(__dirname, '..', 'loaders', 'fileLoader.js')
|
73
|
+
}
|
74
|
+
}
|
75
|
+
};
|
76
|
+
}
|
77
|
+
|
49
78
|
function createNameTemplate(enableChunkHash) {
|
50
79
|
const ext = `${enableChunkHash ? '.[hash:20]' : ''}.[ext]`;
|
51
80
|
return `[name]${ext}`;
|
@@ -16,7 +16,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
16
16
|
let options = (0, _utils.getOptions)();
|
17
17
|
let isWin = process.platform === 'win32';
|
18
18
|
|
19
|
-
let getCSSLoaders = (hasRTL, rtlExclude, classNameBlob, cssUniqueness, selectorReplace, cssHashSelectors, classNamePrefix) => {
|
19
|
+
let getCSSLoaders = (hasRTL, combinerMq, rtlExclude, hoverActive, classNameBlob, cssUniqueness, selectorReplace, cssHashSelectors, classNamePrefix) => {
|
20
20
|
const {
|
21
21
|
devCssFileBountry
|
22
22
|
} = options.app;
|
@@ -28,7 +28,8 @@ let getCSSLoaders = (hasRTL, rtlExclude, classNameBlob, cssUniqueness, selectorR
|
|
28
28
|
} = options.impactService;
|
29
29
|
let rtlExcludeLocal = isWin ? rtlExclude.map(r => r.replace(/\//g, '\\')) : rtlExclude;
|
30
30
|
let cssLoaderOptions = {
|
31
|
-
importLoaders: hasRTL ? 1 : 0,
|
31
|
+
// importLoaders: hasRTL||hoverActive ? 1 : 0,
|
32
|
+
importLoaders: 1,
|
32
33
|
modules: {},
|
33
34
|
sourceMap: true
|
34
35
|
};
|
@@ -50,7 +51,8 @@ let getCSSLoaders = (hasRTL, rtlExclude, classNameBlob, cssUniqueness, selectorR
|
|
50
51
|
return `${prefix} ${selector}`; // Make selectors like [dir=rtl] > .selector
|
51
52
|
}
|
52
53
|
})]
|
53
|
-
})
|
54
|
+
}), // require('postcss-import')(),
|
55
|
+
combinerMq && require('postcss-combine-media-query')(), hoverActive && require('../postcss-plugins/hoverActivePlugin')()].filter(Boolean);
|
54
56
|
return [cssSelectorZipPath && {
|
55
57
|
loader: require.resolve('../loaders/selectorMappingLoader')
|
56
58
|
}, {
|
@@ -3,84 +3,93 @@
|
|
3
3
|
generaly we have manage our all I18n keys and values as language specific i18n files and then we download all i18n in initial page load.
|
4
4
|
|
5
5
|
### what is the problem with this?.
|
6
|
+
|
6
7
|
the problem is i18n file keep get's grown it will affect the the inital page load and critial rendering path.
|
7
8
|
So, We have decide to create a plugin for split i18n per chunk's of js vise.
|
8
9
|
|
9
10
|
### what is i18n split?
|
10
|
-
it is like code split for i18n.
|
11
|
-
we will collect i18n from js chunk and we will create separate i18n chunk per js chunk,
|
12
|
-
with this we already load Js chunks are on demand and now we also download i18n also ondemand with this the initial and forthere actions.
|
13
|
-
when js download's we will download i18n with it.
|
14
11
|
|
15
|
-
|
16
|
-
|
17
|
-
|
12
|
+
it is like code split for i18n.
|
13
|
+
we will collect i18n from js chunk and we will create separate i18n chunk per js chunk,
|
14
|
+
with this we already load Js chunks are on demand and now we also download i18n also ondemand with this the initial and forthere actions.
|
15
|
+
when js download's we will download i18n with it.
|
16
|
+
|
17
|
+
### How to we going to i18n split?
|
18
|
+
|
19
|
+
we will read the js chunk and collect I18n keys form it,
|
20
|
+
then we will create i18n chunk files with these used keys.
|
18
21
|
|
19
22
|
### how do you collect i18n keys from js files?
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
23
|
+
|
24
|
+
we will traverse the js file's AST (static analysis) and find i18 keys,
|
25
|
+
we will concider all string which is in jsResource file as i18n keys.
|
26
|
+
and you can tell dynamic i18n key by js comments,
|
27
|
+
Like Below:-
|
28
|
+
|
29
|
+
```js
|
30
|
+
fetch('tickets?view=view1').then(data => {
|
31
|
+
// I18n support.ticketsEmpty
|
32
|
+
// I18n support.viewNotFount
|
33
|
+
// I18n support.permissionDenied
|
34
|
+
let text = getI18nValue(data.i18nkey);
|
35
|
+
});
|
36
|
+
```
|
32
37
|
|
33
38
|
### is there any posiblity for unwanted keys in some chunk?
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
39
|
+
|
40
|
+
Yes, there is two posiblity
|
41
|
+
|
42
|
+
1. js comment , if you write js comment but it is not needed then it will be add into i18n chunk
|
43
|
+
`To Resolve` we currently do not have perfect idea for this, But we can check this by chunk size limit.
|
44
|
+
`idea's will be welcome`
|
45
|
+
2. like we said "we will concider all string which is in jsResource file as i18n keys",
|
46
|
+
So if you use string not for i18n but it was in jsResource then it will be add into i18n chunk.
|
47
|
+
`To Resolve` this problem we can use some kinda prefix or something.
|
41
48
|
|
42
49
|
### how do we downlowd and give to app?
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
50
|
+
|
51
|
+
we have over write defualt webpack requireEnsure (every dynamic chunk requests are done by that function) function.
|
52
|
+
and we parlarly dowload i18n files. and we ask jsonFunction, to our plugin.
|
53
|
+
So we send i18nkeys , when it was download,
|
54
|
+
you must store all i18n keys, we give asycrnsly by that function download in that patticular.
|
47
55
|
so you must store and update (like append or assign) everytime that function call.
|
48
56
|
|
49
57
|
### how to use this feature?
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
},
|
65
|
-
|
58
|
+
|
59
|
+
to use this feature use have give the below oprtions
|
60
|
+
`package.json`
|
61
|
+
|
62
|
+
```json
|
63
|
+
{
|
64
|
+
/// ...some things
|
65
|
+
"react-cli": {
|
66
|
+
// ...some things
|
67
|
+
"i18n": {
|
68
|
+
"chunkSplitEnable": true,
|
69
|
+
"jsResource": "./deskapp/properties/JSResources.properties",
|
70
|
+
"localeVarName": "window.userInfo.langCode",
|
71
|
+
"jsonpFunc": "window.loadI18nChunk",
|
72
|
+
"templateLabel": "{{--user-locale}}", // this is for html template i18n file path locate template
|
73
|
+
"propertiesFolder": "./deskapp/properties"
|
66
74
|
}
|
75
|
+
// ...some things
|
67
76
|
}
|
68
|
-
|
69
|
-
|
77
|
+
}
|
78
|
+
```
|
70
79
|
|
71
80
|
<!-- not need below -->
|
72
|
-
<!--
|
81
|
+
<!--we have added new feature for css to write logic to how hover related css work in hoverhover and a
|
73
82
|
|
74
83
|
### is there any problems with static analysis?
|
75
|
-
Yes,
|
84
|
+
Yes,
|
76
85
|
|
77
86
|
### how do we use dynamic i18n key?
|
78
87
|
|
79
|
-
and we will download i18n file with
|
88
|
+
and we will download i18n file with
|
80
89
|
|
81
90
|
and when js chunk download's then we will download i18n as need.
|
82
91
|
we all know in our app we loading i18n as one single big file for each locale(language).
|
83
|
-
we are going to split i18n as per js chunk, and when js chunk download's then we will download i18n as need.
|
84
|
-
|
85
|
-
|
92
|
+
we are going to split i18n as per js chunk, and when js chunk download's then we will download i18n as need.
|
93
|
+
|
94
|
+
|
86
95
|
-->
|
@@ -0,0 +1,22 @@
|
|
1
|
+
"use strict";
|
2
|
+
|
3
|
+
const fs = require('fs');
|
4
|
+
|
5
|
+
const postcss = require('postcss');
|
6
|
+
|
7
|
+
function expectRunPostCss(inputFile, outputfile, cb) {
|
8
|
+
const inputStr = fs.readFileSync(inputFile, 'utf-8');
|
9
|
+
postcss(plugins).process(inputStr, {
|
10
|
+
from,
|
11
|
+
to
|
12
|
+
}).then(result => {
|
13
|
+
expect(result.css).toBe(fs.readFileSync(outputfile, 'utf-8'));
|
14
|
+
cb();
|
15
|
+
});
|
16
|
+
}
|
17
|
+
|
18
|
+
describe('To Check Hover active postcss Plugin ', () => {
|
19
|
+
test('should handle normal rule hover', cb => {
|
20
|
+
expectRunPostCss('test1Input.css', 'test1Output.css', cb);
|
21
|
+
});
|
22
|
+
});
|
@@ -0,0 +1,39 @@
|
|
1
|
+
/*Hover_active:ignore*/
|
2
|
+
g,a:hover{
|
3
|
+
color : red
|
4
|
+
}
|
5
|
+
/*Hover:ignore*/
|
6
|
+
h:hover{
|
7
|
+
background : yellow
|
8
|
+
}
|
9
|
+
|
10
|
+
/* Hover_active:ignore */
|
11
|
+
g,d+e:hover{
|
12
|
+
color : black
|
13
|
+
}
|
14
|
+
|
15
|
+
g,d e:hover{
|
16
|
+
color : black
|
17
|
+
}
|
18
|
+
|
19
|
+
@media screen and (max-width:61.25em){
|
20
|
+
/* Hover_active:ignore */
|
21
|
+
a,b,a:hover, b:hover{
|
22
|
+
background-color : blue
|
23
|
+
}
|
24
|
+
a + b,a:hover + b:hover{
|
25
|
+
background-color : blue
|
26
|
+
}
|
27
|
+
a b:hover{
|
28
|
+
background-color : blue
|
29
|
+
}
|
30
|
+
|
31
|
+
.cc:hover {
|
32
|
+
color: red;
|
33
|
+
}
|
34
|
+
|
35
|
+
c:hover{
|
36
|
+
color : red
|
37
|
+
}
|
38
|
+
|
39
|
+
}
|
@@ -0,0 +1,39 @@
|
|
1
|
+
/*Hover_active:ignore*/
|
2
|
+
g,a:hover{
|
3
|
+
color : red
|
4
|
+
}
|
5
|
+
/*Hover:ignore*/
|
6
|
+
h:hover{
|
7
|
+
background : yellow
|
8
|
+
}
|
9
|
+
|
10
|
+
/* Hover_active:ignore */
|
11
|
+
g,d+e:hover{
|
12
|
+
color : black
|
13
|
+
}
|
14
|
+
|
15
|
+
g,d e:hover{
|
16
|
+
color : black
|
17
|
+
}
|
18
|
+
|
19
|
+
@media screen and (max-width:61.25em){
|
20
|
+
/* Hover_active:ignore */
|
21
|
+
a,b,a:hover, b:hover{
|
22
|
+
background-color : blue
|
23
|
+
}
|
24
|
+
a + b,a:hover + b:hover{
|
25
|
+
background-color : blue
|
26
|
+
}
|
27
|
+
a b:hover{
|
28
|
+
background-color : blue
|
29
|
+
}
|
30
|
+
|
31
|
+
.cc:hover {
|
32
|
+
color: red;
|
33
|
+
}
|
34
|
+
|
35
|
+
c:hover{
|
36
|
+
color : red
|
37
|
+
}
|
38
|
+
|
39
|
+
}
|
@@ -0,0 +1,365 @@
|
|
1
|
+
"use strict";
|
2
|
+
|
3
|
+
var _postcss = _interopRequireDefault(require("postcss"));
|
4
|
+
|
5
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
6
|
+
|
7
|
+
/**
|
8
|
+
* we have give support for ignore(exclude) comments
|
9
|
+
* These are the comments' keyword
|
10
|
+
*/
|
11
|
+
const hoverIgnoreQuery = 'Hover:ignore',
|
12
|
+
activeIgnoreQuery = 'Active:ignore',
|
13
|
+
hoverActiveIgnoreQuery = 'HoverActive:ignore';
|
14
|
+
const medHoverIgnoreQuery = 'MedHover:ignore',
|
15
|
+
medActiveIgnoreQuery = 'MedActive:ignore',
|
16
|
+
medHoverActiveIgnoreQuery = 'MedHoverActive:ignore';
|
17
|
+
const hoverMedQuerySuffix = '(min--moz-device-pixel-ratio:0) and (hover: hover), (hover: hover)';
|
18
|
+
const ruleIgnoreCommentRegex = /(Hover:ignore|Active:ignore|HoverActive:ignore)/g;
|
19
|
+
const mediaQueryIgnoreCommentRegex = /(MedHover:ignore|MedActive:ignore|MedHoverActive:ignore)/g;
|
20
|
+
|
21
|
+
function isComment(node) {
|
22
|
+
return node && node.type === 'comment' && node.text !== undefined;
|
23
|
+
}
|
24
|
+
|
25
|
+
function isHoverPresent(atrule) {
|
26
|
+
let hoverPresent = false;
|
27
|
+
atrule.walkRules(rule => {
|
28
|
+
if (rule.selector.includes('hover')) {
|
29
|
+
hoverPresent = true;
|
30
|
+
}
|
31
|
+
});
|
32
|
+
return hoverPresent;
|
33
|
+
}
|
34
|
+
|
35
|
+
module.exports = _postcss.default.plugin('postcss-mobile-hover', () => rootOriginal => {
|
36
|
+
const hoverRules = [];
|
37
|
+
let positionsObj = {};
|
38
|
+
|
39
|
+
function isRuleHasIgnoreComment(index, type) {
|
40
|
+
const prevNode = rootOriginal.nodes[index - 1];
|
41
|
+
|
42
|
+
if (isComment(prevNode)) {
|
43
|
+
return prevNode.text === type;
|
44
|
+
}
|
45
|
+
|
46
|
+
return false;
|
47
|
+
}
|
48
|
+
|
49
|
+
function isMediaQueryHasIgnoreComment(node, type) {
|
50
|
+
if (isComment(node)) {
|
51
|
+
return node.text === type;
|
52
|
+
}
|
53
|
+
|
54
|
+
return false;
|
55
|
+
}
|
56
|
+
|
57
|
+
function hasIgnoreComment({
|
58
|
+
index,
|
59
|
+
atrule,
|
60
|
+
type
|
61
|
+
}) {
|
62
|
+
if (type.match(mediaQueryIgnoreCommentRegex)) {
|
63
|
+
return isMediaQueryHasIgnoreComment(atrule.nodes[index - 1], type.slice(3));
|
64
|
+
}
|
65
|
+
|
66
|
+
if (type.match(ruleIgnoreCommentRegex)) {
|
67
|
+
return isRuleHasIgnoreComment(index, type);
|
68
|
+
}
|
69
|
+
|
70
|
+
return false;
|
71
|
+
}
|
72
|
+
|
73
|
+
function getPositionsOfHoverAndActiveMedQueries(parent) {
|
74
|
+
const allNodes = rootOriginal.nodes;
|
75
|
+
const hoverMediaQuery = `${parent.params} and all and ${hoverMedQuerySuffix}`;
|
76
|
+
const hoverNoneMediaQuery = `${parent.params} and (hover: none)`;
|
77
|
+
const positions = {
|
78
|
+
hovMed: allNodes[positionsObj[hoverMediaQuery]],
|
79
|
+
actMed: allNodes[positionsObj[hoverNoneMediaQuery]]
|
80
|
+
};
|
81
|
+
return positions;
|
82
|
+
}
|
83
|
+
|
84
|
+
function handleMedHoverAndHoverActiveIgnore(atrule, index) {
|
85
|
+
return !hasIgnoreComment({
|
86
|
+
atrule,
|
87
|
+
index,
|
88
|
+
type: medHoverIgnoreQuery
|
89
|
+
}) && !hasIgnoreComment({
|
90
|
+
atrule,
|
91
|
+
index,
|
92
|
+
type: medHoverActiveIgnoreQuery
|
93
|
+
});
|
94
|
+
}
|
95
|
+
|
96
|
+
function handleMedActiveAndHoverActiveIgnore(atrule, index) {
|
97
|
+
return !hasIgnoreComment({
|
98
|
+
atrule,
|
99
|
+
index,
|
100
|
+
type: medActiveIgnoreQuery
|
101
|
+
}) && !hasIgnoreComment({
|
102
|
+
atrule,
|
103
|
+
index,
|
104
|
+
type: medHoverActiveIgnoreQuery
|
105
|
+
});
|
106
|
+
}
|
107
|
+
|
108
|
+
function handleHoverAndHoverActiveIgnore(index) {
|
109
|
+
return !hasIgnoreComment({
|
110
|
+
index,
|
111
|
+
type: hoverIgnoreQuery
|
112
|
+
}) && !hasIgnoreComment({
|
113
|
+
index,
|
114
|
+
type: hoverActiveIgnoreQuery
|
115
|
+
});
|
116
|
+
}
|
117
|
+
|
118
|
+
function handleActiveAndHoverActiveIgnore(index) {
|
119
|
+
return !hasIgnoreComment({
|
120
|
+
index,
|
121
|
+
type: activeIgnoreQuery
|
122
|
+
}) && !hasIgnoreComment({
|
123
|
+
index,
|
124
|
+
type: hoverActiveIgnoreQuery
|
125
|
+
});
|
126
|
+
}
|
127
|
+
|
128
|
+
function handleAllIgnoreCases(index) {
|
129
|
+
return !hasIgnoreComment({
|
130
|
+
index,
|
131
|
+
type: activeIgnoreQuery
|
132
|
+
}) && !hasIgnoreComment({
|
133
|
+
index,
|
134
|
+
type: hoverIgnoreQuery
|
135
|
+
}) && !hasIgnoreComment({
|
136
|
+
index,
|
137
|
+
type: hoverActiveIgnoreQuery
|
138
|
+
});
|
139
|
+
}
|
140
|
+
|
141
|
+
function mediaCommaQuery(rule, index) {
|
142
|
+
if (rule.parent.params !== undefined && !rule.parent.params.includes('hover')) {
|
143
|
+
//console.log(hovMed, actMed);
|
144
|
+
let newSelector = '';
|
145
|
+
let {
|
146
|
+
hovMed,
|
147
|
+
actMed
|
148
|
+
} = getPositionsOfHoverAndActiveMedQueries(rule.parent);
|
149
|
+
let hovQueries = [];
|
150
|
+
let actQueries = [];
|
151
|
+
rule.selector.split(/\s*,\s*/).forEach(_subrule => {
|
152
|
+
let subrule = _subrule.trim();
|
153
|
+
|
154
|
+
let clone = rule.clone();
|
155
|
+
|
156
|
+
if (subrule.includes('hover')) {
|
157
|
+
clone.selector = subrule;
|
158
|
+
|
159
|
+
if (handleMedHoverAndHoverActiveIgnore(rule.parent, index)) {
|
160
|
+
hovQueries.push(subrule);
|
161
|
+
}
|
162
|
+
|
163
|
+
if (handleMedActiveAndHoverActiveIgnore(rule.parent, index)) {
|
164
|
+
actQueries.push(subrule);
|
165
|
+
}
|
166
|
+
} else {
|
167
|
+
newSelector += `${subrule}, `;
|
168
|
+
}
|
169
|
+
});
|
170
|
+
|
171
|
+
if (hovQueries.length > 0) {
|
172
|
+
let clone = rule.clone();
|
173
|
+
clone.selector = hovQueries.join(',');
|
174
|
+
hovMed.append(clone);
|
175
|
+
}
|
176
|
+
|
177
|
+
if (actQueries.length > 0) {
|
178
|
+
let clone = rule.clone();
|
179
|
+
clone.selector = actQueries.join(',');
|
180
|
+
actMed.append(clone.clone({
|
181
|
+
selector: clone.selector.replace(/:hover/gi, ':active')
|
182
|
+
}));
|
183
|
+
}
|
184
|
+
|
185
|
+
if (handleMedHoverAndHoverActiveIgnore(rule.parent, index)) {
|
186
|
+
rule.selector = newSelector.substring(0, newSelector.length - 2);
|
187
|
+
}
|
188
|
+
|
189
|
+
if (rule.selector === '') {
|
190
|
+
rule.remove();
|
191
|
+
}
|
192
|
+
}
|
193
|
+
}
|
194
|
+
|
195
|
+
function mediaQuery(rule, index) {
|
196
|
+
if (rule.parent.params !== undefined && !rule.parent.params.includes('hover')) {
|
197
|
+
let {
|
198
|
+
hovMed,
|
199
|
+
actMed
|
200
|
+
} = getPositionsOfHoverAndActiveMedQueries(rule.parent);
|
201
|
+
|
202
|
+
if (rule.selector.includes('hover') && hovMed !== undefined && rule.parent.type === 'atrule') {
|
203
|
+
if (handleMedHoverAndHoverActiveIgnore(rule.parent, index)) {
|
204
|
+
hovMed.append(rule);
|
205
|
+
}
|
206
|
+
|
207
|
+
if (handleMedActiveAndHoverActiveIgnore(rule.parent, index)) {
|
208
|
+
actMed.append(rule.clone({
|
209
|
+
selector: rule.selector.replace(/:hover/gi, ':active')
|
210
|
+
}));
|
211
|
+
}
|
212
|
+
}
|
213
|
+
}
|
214
|
+
}
|
215
|
+
|
216
|
+
function commaQuery(rule, index) {
|
217
|
+
//console.log("comma" , rule.selector.split('\n'));
|
218
|
+
let newSelector = '';
|
219
|
+
let hovQueries = [];
|
220
|
+
rule.selector.split(/\s*,\s*/).forEach(_subrule => {
|
221
|
+
let subrule = _subrule.trim();
|
222
|
+
|
223
|
+
if (subrule.includes('hover')) {
|
224
|
+
// hoverRules.push({ rule: clone, index });
|
225
|
+
hovQueries.push(subrule);
|
226
|
+
} else {
|
227
|
+
newSelector += `${subrule}, `;
|
228
|
+
}
|
229
|
+
});
|
230
|
+
|
231
|
+
if (hovQueries.length > 0) {
|
232
|
+
let clone = rule.clone();
|
233
|
+
clone.selector = hovQueries.join(',');
|
234
|
+
hoverRules.push({
|
235
|
+
rule: clone,
|
236
|
+
index
|
237
|
+
});
|
238
|
+
}
|
239
|
+
|
240
|
+
if (handleHoverAndHoverActiveIgnore(index)) {
|
241
|
+
rule.selector = newSelector.substring(0, newSelector.length - 2).trim();
|
242
|
+
}
|
243
|
+
|
244
|
+
if (rule.selector === '') {
|
245
|
+
rule.remove();
|
246
|
+
}
|
247
|
+
} // Start by identifying all :hover rules
|
248
|
+
|
249
|
+
|
250
|
+
rootOriginal.walkAtRules(atrule => {
|
251
|
+
const hoverQuery = `${atrule.params} and all and ${hoverMedQuerySuffix}`;
|
252
|
+
const activeQuery = `${atrule.params} and (hover: none)`;
|
253
|
+
|
254
|
+
if (isHoverPresent(atrule)) {
|
255
|
+
if (!positionsObj[hoverQuery] && !positionsObj[activeQuery]) {
|
256
|
+
rootOriginal.append({
|
257
|
+
name: 'media',
|
258
|
+
params: hoverQuery
|
259
|
+
});
|
260
|
+
positionsObj[hoverQuery] = rootOriginal.nodes.length - 1;
|
261
|
+
rootOriginal.append({
|
262
|
+
name: 'media',
|
263
|
+
params: activeQuery
|
264
|
+
});
|
265
|
+
positionsObj[activeQuery] = rootOriginal.nodes.length - 1;
|
266
|
+
}
|
267
|
+
}
|
268
|
+
});
|
269
|
+
rootOriginal.walkRules(/:hover/i, (rule, index) => {
|
270
|
+
// media hover query with ',' ' ' '+'
|
271
|
+
// console.log("media query" , rule.selector)
|
272
|
+
if (rule.parent.type === 'atrule' && rule.selector.includes(',')) {
|
273
|
+
//console.log("media comma", rule.selector)
|
274
|
+
mediaCommaQuery(rule, index);
|
275
|
+
} else {
|
276
|
+
// plus, space and other media queries
|
277
|
+
//console.log("media", rule.selector)
|
278
|
+
mediaQuery(rule, index);
|
279
|
+
} // usual hover query
|
280
|
+
|
281
|
+
|
282
|
+
if (!rule.selector.match(/,+| +|\++/g) && rule.parent !== undefined && rule.parent.name === undefined) {
|
283
|
+
hoverRules.push({
|
284
|
+
rule,
|
285
|
+
index
|
286
|
+
});
|
287
|
+
} //usual hover query with ',' ' ' '+'
|
288
|
+
|
289
|
+
|
290
|
+
if (rule.selector.match(/,+| +|\++/g) && rule.parent.name === undefined) {
|
291
|
+
if (rule.selector.includes(',')) {
|
292
|
+
commaQuery(rule, index);
|
293
|
+
} else if (rule.selector.match(/ +|\++/g)) {
|
294
|
+
//console.log("plus or space" , rule.selector);
|
295
|
+
if (rule.selector.includes('hover')) {
|
296
|
+
hoverRules.push({
|
297
|
+
rule,
|
298
|
+
index
|
299
|
+
}); //rule.remove();
|
300
|
+
}
|
301
|
+
}
|
302
|
+
}
|
303
|
+
}); // If there are any :hover rules in the input, then create media queries
|
304
|
+
// to automatically translate it into :active on touch-based devices
|
305
|
+
|
306
|
+
if (hoverRules.length > 0) {
|
307
|
+
// Create a media query targetting devices that actually support
|
308
|
+
// hover
|
309
|
+
const hoverQuery = rootOriginal.append({
|
310
|
+
name: 'media',
|
311
|
+
params: `all and ${hoverMedQuerySuffix}`
|
312
|
+
}).last; // Create a media query targetting devices that don't support hover
|
313
|
+
// (ie. devices where we should fall back to :active instead)
|
314
|
+
|
315
|
+
const activeQuery = rootOriginal.append({
|
316
|
+
name: 'media',
|
317
|
+
params: '(hover: none)'
|
318
|
+
}).last; // Loop through the hover rules and apply them to each of the media
|
319
|
+
// queries
|
320
|
+
// eslint-disable-next-line no-labels
|
321
|
+
|
322
|
+
outerLoop: for (const hoverRule of hoverRules) {
|
323
|
+
// determine if the rule has been nested inside another media
|
324
|
+
// query; in that case bail out as we have no way of reliably
|
325
|
+
// nesting these queries
|
326
|
+
let parentRule = hoverRule.rule.parent;
|
327
|
+
|
328
|
+
while (parentRule) {
|
329
|
+
if (parentRule.type === 'atrule' && parentRule.name === 'media') {
|
330
|
+
// eslint-disable-next-line no-labels
|
331
|
+
continue outerLoop;
|
332
|
+
}
|
333
|
+
|
334
|
+
parentRule = parentRule.parent;
|
335
|
+
} // Push a clone of the :hover rule 'as is' to queries where we
|
336
|
+
// expect the user's device to support hover
|
337
|
+
// ieQuery.append(hoverRule.clone());
|
338
|
+
|
339
|
+
|
340
|
+
if (handleHoverAndHoverActiveIgnore(hoverRule.index)) {
|
341
|
+
hoverQuery.append(hoverRule.rule.clone());
|
342
|
+
} // Push a clone of the :hover rule, where we transform the
|
343
|
+
// selector to :active to the query targetting devices that
|
344
|
+
// don't support hover
|
345
|
+
|
346
|
+
|
347
|
+
if (handleActiveAndHoverActiveIgnore(hoverRule.index)) {
|
348
|
+
activeQuery.append(hoverRule.rule.clone({
|
349
|
+
selector: hoverRule.rule.selector.replace(/:hover/gi, ':active')
|
350
|
+
}));
|
351
|
+
} // remove legacy rule from output
|
352
|
+
|
353
|
+
|
354
|
+
if (handleAllIgnoreCases(hoverRule.index)) {
|
355
|
+
hoverRule.rule.remove();
|
356
|
+
}
|
357
|
+
}
|
358
|
+
}
|
359
|
+
|
360
|
+
rootOriginal.walkAtRules(atrule => {
|
361
|
+
if (atrule !== undefined && atrule.nodes !== undefined && atrule.nodes.length === 0 || atrule.nodes === undefined) {
|
362
|
+
atrule.remove();
|
363
|
+
}
|
364
|
+
});
|
365
|
+
});
|
package/lib/schemas/index.js
CHANGED
@@ -71,7 +71,10 @@ var _default = {
|
|
71
71
|
},
|
72
72
|
createSDkFile: false,
|
73
73
|
nameScope: 'ZOHODESK',
|
74
|
-
version:
|
74
|
+
version: {
|
75
|
+
value: 'stable',
|
76
|
+
cli: 'efc_version'
|
77
|
+
},
|
75
78
|
outputFile: {
|
76
79
|
value: 'efc-sdk-[version].js',
|
77
80
|
cli: 'efc_output_file'
|
@@ -218,6 +221,8 @@ var _default = {
|
|
218
221
|
value: 'zd',
|
219
222
|
cli: 'class_prefix'
|
220
223
|
},
|
224
|
+
combinerMq: false,
|
225
|
+
hoverActive: false,
|
221
226
|
selectorReplace: null,
|
222
227
|
devConsoleExculde: {
|
223
228
|
value: false,
|
@@ -271,6 +276,8 @@ var _default = {
|
|
271
276
|
cli: 'css_unique'
|
272
277
|
},
|
273
278
|
enableChunkHash: false,
|
279
|
+
combinerMq: false,
|
280
|
+
hoverActive: false,
|
274
281
|
folder: 'src',
|
275
282
|
disableES5Transpile: false,
|
276
283
|
hasRTL: false,
|
@@ -13,18 +13,16 @@ var _fs = require("fs");
|
|
13
13
|
|
14
14
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
15
15
|
|
16
|
-
|
16
|
+
const appPath = process.cwd();
|
17
17
|
const isNodeModuleUnderAppFolder = __dirname.indexOf(appPath) !== -1;
|
18
|
-
|
18
|
+
const isWindows = (0, _os.platform)().toLowerCase() === 'win32';
|
19
19
|
|
20
|
-
const _getCliPath = !isNodeModuleUnderAppFolder ? libName =>
|
21
|
-
const cliPath = _path.default.join(__dirname, '..', '..', 'node_modules', '.bin', libName);
|
22
|
-
|
23
|
-
return (0, _fs.existsSync)(cliPath) ? cliPath : libName;
|
24
|
-
} : libName => libName;
|
20
|
+
const _getCliPath = !isNodeModuleUnderAppFolder ? libName => _path.default.join(__dirname, '..', '..', 'node_modules', '.bin', libName) : libName => libName;
|
25
21
|
|
26
22
|
const suffixExt = isWindows ? '.cmd' : '';
|
27
23
|
|
28
24
|
function getCliPath(libName) {
|
29
|
-
|
25
|
+
const cliPath = _getCliPath(libName + suffixExt);
|
26
|
+
|
27
|
+
return (0, _fs.existsSync)(cliPath) ? cliPath : libName;
|
30
28
|
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@zohodesk/react-cli",
|
3
|
-
"version": "0.0.1-exp.
|
3
|
+
"version": "0.0.1-exp.169.1",
|
4
4
|
"description": "A CLI tool for build modern web application and libraries",
|
5
5
|
"scripts": {
|
6
6
|
"init": "node ./lib/utils/init.js",
|
@@ -91,8 +91,11 @@
|
|
91
91
|
"nodemon": "2.0.4",
|
92
92
|
"optimize-js": "1.0.3",
|
93
93
|
"postcss": "7.0.32",
|
94
|
+
"postcss-combine-media-query": "1.0.1",
|
94
95
|
"postcss-hash-classname": "0.4.0",
|
96
|
+
"postcss-import": "14.1.0",
|
95
97
|
"postcss-loader": "3.0.0",
|
98
|
+
"postcss-mobile-hover": "1.0.2",
|
96
99
|
"postcss-selector-replace": "1.0.2",
|
97
100
|
"prop-types": "15.7.2",
|
98
101
|
"react": "16.13.1",
|