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

feat: use css2json loader by default #1076

Merged
merged 6 commits into from
Oct 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ bundle-config-loader.js
xml-namespace-loader.d.ts
xml-namespace-loader.js

css2json-loader.d.ts
css2json-loader.js

**/*.spec.js*
**/*.spec.d.ts*

Expand Down
41 changes: 33 additions & 8 deletions apply-css-loader.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,43 @@
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";

function checkForCssLoader(loaders, loaderIndex) {
if (!loaders) {
return false;
}

for (var i = loaderIndex + 1; i < loaders.length; i++) {
const loader = loaders[i];
if (loader.path && loader.path.indexOf("css-loader") > 0) {
return true;
}
}

return false;
}

module.exports = function (content, map) {
if (this.request.match(/\/app\.(css|scss|less|sass)$/)) {
return content;
}

// Emit a warning if the file was not processed by the css-loader.
if (!checkForCssLoader(this.loaders, this.loaderIndex)) {
this.emitWarning(new Error(cssLoaderWarning));
}

content += `
const application = require("tns-core-modules/application");
require("tns-core-modules/ui/styling/style-scope");

exports.forEach(cssExport => {
if (cssExport.length > 1 && cssExport[1]) {
// applying the second item of the export as it contains the css contents
application.addCss(cssExport[1]);
}
});
`;
if (typeof exports.forEach === "function") {
exports.forEach(cssExport => {
if (cssExport.length > 1 && cssExport[1]) {
// applying the second item of the export as it contains the css contents
application.addCss(cssExport[1]);
}
});
}
`;

this.callback(null, content, map);
}
}
29 changes: 0 additions & 29 deletions css2json-loader.js

This file was deleted.

79 changes: 79 additions & 0 deletions css2json-loader.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import css2jsonLoader from "./css2json-loader";

const importTestCases = [
`@import url("custom.css");`,
`@import url('custom.css');`,
`@import url('custom.css') print;`,
`@import url("custom.css") print;`,
`@import url('custom.css') screen and (orientation:landscape);`,
`@import url("custom.css") screen and (orientation:landscape);`,
`@import 'custom.css';`,
`@import "custom.css";`,
`@import 'custom.css' screen;`,
`@import "custom.css" screen;`,
`@import url(custom.css);`,
]

const someCSS = `
.btn {
background-color: #7f9
}
`

describe("css2jsonLoader", () => {
it("converts CSS to JSON", (done) => {
const loaderContext = {
callback: (error, source: string, map) => {
expect(source).toContain(`{"type":"declaration","property":"background-color","value":"#7f9"}`);

done();
}
}

css2jsonLoader.call(loaderContext, someCSS);
})

importTestCases.forEach((importTestCase) => {
it(`handles: ${importTestCase}`, (done) => {

const loaderContext = {
callback: (error, source: string, map) => {
expect(source).toContain(`global.registerModule("custom.css", () => require("custom.css"))`);
expect(source).toContain(`{"type":"declaration","property":"background-color","value":"#7f9"}`);

done();
},
}

css2jsonLoader.call(loaderContext, importTestCase + someCSS);
})
});

it("inlines css2json loader in imports if option is provided", (done) => {
const loaderContext = {
callback: (error, source: string, map) => {
expect(source).toContain(`global.registerModule("custom.css", () => require("!nativescript-dev-webpack/css2json-loader?useForImports!custom.css"))`);
expect(source).toContain(`{"type":"declaration","property":"background-color","value":"#7f9"}`);

done();
},
query: { useForImports: true }
}

css2jsonLoader.call(loaderContext, `@import url("custom.css");` + someCSS);
})

it("registers modules for paths starting with ~", (done) => {
const loaderContext = {
callback: (error, source: string, map) => {
expect(source).toContain(`global.registerModule("~custom.css", () => require("custom.css"))`);
expect(source).toContain(`global.registerModule("custom.css", () => require("custom.css"))`);
expect(source).toContain(`{"type":"declaration","property":"background-color","value":"#7f9"}`);

done();
}
}

css2jsonLoader.call(loaderContext, `@import url("~custom.css");` + someCSS);
})
});
66 changes: 66 additions & 0 deletions css2json-loader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { parse, Rule, SyntaxTree } from "tns-core-modules/css";
import { loader } from "webpack";
import { getOptions } from "loader-utils";

interface ImportRule extends Rule {
import: string;
}

const betweenQuotesPattern = /('|")(.*?)\1/;
const unpackUrlPattern = /url\(([^\)]+)\)/;
const inlineLoader = "!nativescript-dev-webpack/css2json-loader?useForImports!"

const loader: loader.Loader = function (content: string, map) {
const options = getOptions(this) || {};
const inline = !!options.useForImports;
const requirePrefix = inline ? inlineLoader : "";

const ast = parse(content, undefined);

let dependencies = [];
getImportRules(ast)
.map(extractUrlFromRule)
.map(createRequireUri)
.forEach(({ uri, requireURI }) => {
dependencies.push(`global.registerModule("${uri}", () => require("${requirePrefix}${requireURI}"));`);

// Call registerModule with requireURI to handle cases like @import "~@nativescript/theme/css/blue.css";
if (uri !== requireURI) {
dependencies.push(`global.registerModule("${requireURI}", () => require("${requirePrefix}${requireURI}"));`);
}
});
const str = JSON.stringify(ast, (k, v) => k === "position" ? undefined : v);
this.callback(null, `${dependencies.join("\n")}module.exports = ${str};`, map);
}

function getImportRules(ast: SyntaxTree): ImportRule[] {
if (!ast || (<any>ast).type !== "stylesheet" || !ast.stylesheet) {
return [];
}
return <ImportRule[]>ast.stylesheet.rules
.filter(rule => rule.type === "import" && (<any>rule).import)
}

/**
* Extracts the url from import rule (ex. `url("./platform.css")`)
*/
function extractUrlFromRule(importRule: ImportRule): string {
const urlValue = importRule.import;

const unpackedUrlMatch = urlValue.match(unpackUrlPattern);
const unpackedValue = unpackedUrlMatch ? unpackedUrlMatch[1] : urlValue

const quotesMatch = unpackedValue.match(betweenQuotesPattern);
return quotesMatch ? quotesMatch[2] : unpackedValue;
};

function createRequireUri(uri): { uri: string, requireURI: string } {
return {
uri: uri,
requireURI: uri[0] === "~" && uri[1] !== "/" ? uri.substr(1) : uri
};
}



export default loader;
10 changes: 8 additions & 2 deletions demo/AngularApp/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,14 +226,20 @@ module.exports = env => {
test: /[\/|\\]app\.css$/,
use: [
"nativescript-dev-webpack/style-hot-loader",
{ loader: "css-loader", options: { url: false } }
{
loader: "nativescript-dev-webpack/css2json-loader",
options: { useForImports: true }
}
]
},
{
test: /[\/|\\]app\.scss$/,
use: [
"nativescript-dev-webpack/style-hot-loader",
{ loader: "css-loader", options: { url: false } },
{
loader: "nativescript-dev-webpack/css2json-loader",
options: { useForImports: true }
},
"sass-loader"
]
},
Expand Down
4 changes: 2 additions & 2 deletions demo/JavaScriptApp/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,13 +188,13 @@ module.exports = env => {

{
test: /\.css$/,
use: { loader: "css-loader", options: { url: false } }
use: "nativescript-dev-webpack/css2json-loader"
},

{
test: /\.scss$/,
use: [
{ loader: "css-loader", options: { url: false } },
"nativescript-dev-webpack/css2json-loader",
"sass-loader"
]
},
Expand Down
4 changes: 2 additions & 2 deletions demo/TypeScriptApp/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,13 +194,13 @@ module.exports = env => {

{
test: /\.css$/,
use: { loader: "css-loader", options: { url: false } }
use: "nativescript-dev-webpack/css2json-loader"
},

{
test: /\.scss$/,
use: [
{ loader: "css-loader", options: { url: false } },
"nativescript-dev-webpack/css2json-loader",
"sass-loader"
]
},
Expand Down
11 changes: 8 additions & 3 deletions templates/webpack.angular.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,19 +228,24 @@ module.exports = env => {

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

// tns-core-modules reads the app.css and its imports using css-loader
{
test: /[\/|\\]app\.css$/,
use: [
"nativescript-dev-webpack/style-hot-loader",
{ loader: "css-loader", options: { url: false } }
{
loader: "nativescript-dev-webpack/css2json-loader",
options: { useForImports: true }
}
]
},
{
test: /[\/|\\]app\.scss$/,
use: [
"nativescript-dev-webpack/style-hot-loader",
{ loader: "css-loader", options: { url: false } },
{
loader: "nativescript-dev-webpack/css2json-loader",
options: { useForImports: true }
},
"sass-loader"
]
},
Expand Down
4 changes: 2 additions & 2 deletions templates/webpack.javascript.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,13 +195,13 @@ module.exports = env => {

{
test: /\.css$/,
use: { loader: "css-loader", options: { url: false } }
use: "nativescript-dev-webpack/css2json-loader"
},

{
test: /\.scss$/,
use: [
{ loader: "css-loader", options: { url: false } },
"nativescript-dev-webpack/css2json-loader",
"sass-loader"
]
},
Expand Down
4 changes: 2 additions & 2 deletions templates/webpack.typescript.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,13 +202,13 @@ module.exports = env => {

{
test: /\.css$/,
use: { loader: "css-loader", options: { url: false } }
use: "nativescript-dev-webpack/css2json-loader"
},

{
test: /\.scss$/,
use: [
{ loader: "css-loader", options: { url: false } },
"nativescript-dev-webpack/css2json-loader",
"sass-loader"
]
},
Expand Down
Loading