Skip to content
This repository was archived by the owner on Aug 7, 2021. It is now read-only.

Commit c07b8b8

Browse files
Merge pull request #1076 from NativeScript/css2json
feat: use css2json loader by default
2 parents b93211d + 1e38358 commit c07b8b8

12 files changed

+229
-51
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ bundle-config-loader.js
3434
xml-namespace-loader.d.ts
3535
xml-namespace-loader.js
3636

37+
css2json-loader.d.ts
38+
css2json-loader.js
39+
3740
**/*.spec.js*
3841
**/*.spec.d.ts*
3942

apply-css-loader.js

+33-8
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,43 @@
1+
const cssLoaderWarning = "The apply-css-loader expects the file to be pre-processed by css-loader. It might not work properly. Please check your webpack configuration";
2+
3+
function checkForCssLoader(loaders, loaderIndex) {
4+
if (!loaders) {
5+
return false;
6+
}
7+
8+
for (var i = loaderIndex + 1; i < loaders.length; i++) {
9+
const loader = loaders[i];
10+
if (loader.path && loader.path.indexOf("css-loader") > 0) {
11+
return true;
12+
}
13+
}
14+
15+
return false;
16+
}
17+
118
module.exports = function (content, map) {
219
if (this.request.match(/\/app\.(css|scss|less|sass)$/)) {
320
return content;
421
}
22+
23+
// Emit a warning if the file was not processed by the css-loader.
24+
if (!checkForCssLoader(this.loaders, this.loaderIndex)) {
25+
this.emitWarning(new Error(cssLoaderWarning));
26+
}
27+
528
content += `
629
const application = require("tns-core-modules/application");
730
require("tns-core-modules/ui/styling/style-scope");
831
9-
exports.forEach(cssExport => {
10-
if (cssExport.length > 1 && cssExport[1]) {
11-
// applying the second item of the export as it contains the css contents
12-
application.addCss(cssExport[1]);
13-
}
14-
});
15-
`;
32+
if (typeof exports.forEach === "function") {
33+
exports.forEach(cssExport => {
34+
if (cssExport.length > 1 && cssExport[1]) {
35+
// applying the second item of the export as it contains the css contents
36+
application.addCss(cssExport[1]);
37+
}
38+
});
39+
}
40+
`;
1641

1742
this.callback(null, content, map);
18-
}
43+
}

css2json-loader.js

-29
This file was deleted.

css2json-loader.spec.ts

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import css2jsonLoader from "./css2json-loader";
2+
3+
const importTestCases = [
4+
`@import url("custom.css");`,
5+
`@import url('custom.css');`,
6+
`@import url('custom.css') print;`,
7+
`@import url("custom.css") print;`,
8+
`@import url('custom.css') screen and (orientation:landscape);`,
9+
`@import url("custom.css") screen and (orientation:landscape);`,
10+
`@import 'custom.css';`,
11+
`@import "custom.css";`,
12+
`@import 'custom.css' screen;`,
13+
`@import "custom.css" screen;`,
14+
`@import url(custom.css);`,
15+
]
16+
17+
const someCSS = `
18+
.btn {
19+
background-color: #7f9
20+
}
21+
`
22+
23+
describe("css2jsonLoader", () => {
24+
it("converts CSS to JSON", (done) => {
25+
const loaderContext = {
26+
callback: (error, source: string, map) => {
27+
expect(source).toContain(`{"type":"declaration","property":"background-color","value":"#7f9"}`);
28+
29+
done();
30+
}
31+
}
32+
33+
css2jsonLoader.call(loaderContext, someCSS);
34+
})
35+
36+
importTestCases.forEach((importTestCase) => {
37+
it(`handles: ${importTestCase}`, (done) => {
38+
39+
const loaderContext = {
40+
callback: (error, source: string, map) => {
41+
expect(source).toContain(`global.registerModule("custom.css", () => require("custom.css"))`);
42+
expect(source).toContain(`{"type":"declaration","property":"background-color","value":"#7f9"}`);
43+
44+
done();
45+
},
46+
}
47+
48+
css2jsonLoader.call(loaderContext, importTestCase + someCSS);
49+
})
50+
});
51+
52+
it("inlines css2json loader in imports if option is provided", (done) => {
53+
const loaderContext = {
54+
callback: (error, source: string, map) => {
55+
expect(source).toContain(`global.registerModule("custom.css", () => require("!nativescript-dev-webpack/css2json-loader?useForImports!custom.css"))`);
56+
expect(source).toContain(`{"type":"declaration","property":"background-color","value":"#7f9"}`);
57+
58+
done();
59+
},
60+
query: { useForImports: true }
61+
}
62+
63+
css2jsonLoader.call(loaderContext, `@import url("custom.css");` + someCSS);
64+
})
65+
66+
it("registers modules for paths starting with ~", (done) => {
67+
const loaderContext = {
68+
callback: (error, source: string, map) => {
69+
expect(source).toContain(`global.registerModule("~custom.css", () => require("custom.css"))`);
70+
expect(source).toContain(`global.registerModule("custom.css", () => require("custom.css"))`);
71+
expect(source).toContain(`{"type":"declaration","property":"background-color","value":"#7f9"}`);
72+
73+
done();
74+
}
75+
}
76+
77+
css2jsonLoader.call(loaderContext, `@import url("~custom.css");` + someCSS);
78+
})
79+
});

css2json-loader.ts

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { parse, Rule, SyntaxTree } from "tns-core-modules/css";
2+
import { loader } from "webpack";
3+
import { getOptions } from "loader-utils";
4+
5+
interface ImportRule extends Rule {
6+
import: string;
7+
}
8+
9+
const betweenQuotesPattern = /('|")(.*?)\1/;
10+
const unpackUrlPattern = /url\(([^\)]+)\)/;
11+
const inlineLoader = "!nativescript-dev-webpack/css2json-loader?useForImports!"
12+
13+
const loader: loader.Loader = function (content: string, map) {
14+
const options = getOptions(this) || {};
15+
const inline = !!options.useForImports;
16+
const requirePrefix = inline ? inlineLoader : "";
17+
18+
const ast = parse(content, undefined);
19+
20+
let dependencies = [];
21+
getImportRules(ast)
22+
.map(extractUrlFromRule)
23+
.map(createRequireUri)
24+
.forEach(({ uri, requireURI }) => {
25+
dependencies.push(`global.registerModule("${uri}", () => require("${requirePrefix}${requireURI}"));`);
26+
27+
// Call registerModule with requireURI to handle cases like @import "~@nativescript/theme/css/blue.css";
28+
if (uri !== requireURI) {
29+
dependencies.push(`global.registerModule("${requireURI}", () => require("${requirePrefix}${requireURI}"));`);
30+
}
31+
});
32+
const str = JSON.stringify(ast, (k, v) => k === "position" ? undefined : v);
33+
this.callback(null, `${dependencies.join("\n")}module.exports = ${str};`, map);
34+
}
35+
36+
function getImportRules(ast: SyntaxTree): ImportRule[] {
37+
if (!ast || (<any>ast).type !== "stylesheet" || !ast.stylesheet) {
38+
return [];
39+
}
40+
return <ImportRule[]>ast.stylesheet.rules
41+
.filter(rule => rule.type === "import" && (<any>rule).import)
42+
}
43+
44+
/**
45+
* Extracts the url from import rule (ex. `url("./platform.css")`)
46+
*/
47+
function extractUrlFromRule(importRule: ImportRule): string {
48+
const urlValue = importRule.import;
49+
50+
const unpackedUrlMatch = urlValue.match(unpackUrlPattern);
51+
const unpackedValue = unpackedUrlMatch ? unpackedUrlMatch[1] : urlValue
52+
53+
const quotesMatch = unpackedValue.match(betweenQuotesPattern);
54+
return quotesMatch ? quotesMatch[2] : unpackedValue;
55+
};
56+
57+
function createRequireUri(uri): { uri: string, requireURI: string } {
58+
return {
59+
uri: uri,
60+
requireURI: uri[0] === "~" && uri[1] !== "/" ? uri.substr(1) : uri
61+
};
62+
}
63+
64+
65+
66+
export default loader;

demo/AngularApp/webpack.config.js

+8-2
Original file line numberDiff line numberDiff line change
@@ -226,14 +226,20 @@ module.exports = env => {
226226
test: /[\/|\\]app\.css$/,
227227
use: [
228228
"nativescript-dev-webpack/style-hot-loader",
229-
{ loader: "css-loader", options: { url: false } }
229+
{
230+
loader: "nativescript-dev-webpack/css2json-loader",
231+
options: { useForImports: true }
232+
}
230233
]
231234
},
232235
{
233236
test: /[\/|\\]app\.scss$/,
234237
use: [
235238
"nativescript-dev-webpack/style-hot-loader",
236-
{ loader: "css-loader", options: { url: false } },
239+
{
240+
loader: "nativescript-dev-webpack/css2json-loader",
241+
options: { useForImports: true }
242+
},
237243
"sass-loader"
238244
]
239245
},

demo/JavaScriptApp/webpack.config.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -188,13 +188,13 @@ module.exports = env => {
188188

189189
{
190190
test: /\.css$/,
191-
use: { loader: "css-loader", options: { url: false } }
191+
use: "nativescript-dev-webpack/css2json-loader"
192192
},
193193

194194
{
195195
test: /\.scss$/,
196196
use: [
197-
{ loader: "css-loader", options: { url: false } },
197+
"nativescript-dev-webpack/css2json-loader",
198198
"sass-loader"
199199
]
200200
},

demo/TypeScriptApp/webpack.config.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -194,13 +194,13 @@ module.exports = env => {
194194

195195
{
196196
test: /\.css$/,
197-
use: { loader: "css-loader", options: { url: false } }
197+
use: "nativescript-dev-webpack/css2json-loader"
198198
},
199199

200200
{
201201
test: /\.scss$/,
202202
use: [
203-
{ loader: "css-loader", options: { url: false } },
203+
"nativescript-dev-webpack/css2json-loader",
204204
"sass-loader"
205205
]
206206
},

templates/webpack.angular.js

+8-3
Original file line numberDiff line numberDiff line change
@@ -228,19 +228,24 @@ module.exports = env => {
228228

229229
{ test: /\.html$|\.xml$/, use: "raw-loader" },
230230

231-
// tns-core-modules reads the app.css and its imports using css-loader
232231
{
233232
test: /[\/|\\]app\.css$/,
234233
use: [
235234
"nativescript-dev-webpack/style-hot-loader",
236-
{ loader: "css-loader", options: { url: false } }
235+
{
236+
loader: "nativescript-dev-webpack/css2json-loader",
237+
options: { useForImports: true }
238+
}
237239
]
238240
},
239241
{
240242
test: /[\/|\\]app\.scss$/,
241243
use: [
242244
"nativescript-dev-webpack/style-hot-loader",
243-
{ loader: "css-loader", options: { url: false } },
245+
{
246+
loader: "nativescript-dev-webpack/css2json-loader",
247+
options: { useForImports: true }
248+
},
244249
"sass-loader"
245250
]
246251
},

templates/webpack.javascript.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -195,13 +195,13 @@ module.exports = env => {
195195

196196
{
197197
test: /\.css$/,
198-
use: { loader: "css-loader", options: { url: false } }
198+
use: "nativescript-dev-webpack/css2json-loader"
199199
},
200200

201201
{
202202
test: /\.scss$/,
203203
use: [
204-
{ loader: "css-loader", options: { url: false } },
204+
"nativescript-dev-webpack/css2json-loader",
205205
"sass-loader"
206206
]
207207
},

templates/webpack.typescript.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -202,13 +202,13 @@ module.exports = env => {
202202

203203
{
204204
test: /\.css$/,
205-
use: { loader: "css-loader", options: { url: false } }
205+
use: "nativescript-dev-webpack/css2json-loader"
206206
},
207207

208208
{
209209
test: /\.scss$/,
210210
use: [
211-
{ loader: "css-loader", options: { url: false } },
211+
"nativescript-dev-webpack/css2json-loader",
212212
"sass-loader"
213213
]
214214
},

0 commit comments

Comments
 (0)