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

Commit ea29bb6

Browse files
Add FS, PlatformSuffixPlugin and css2json-loader (#290)
1 parent 0ee667d commit ea29bb6

File tree

5 files changed

+195
-1
lines changed

5 files changed

+195
-1
lines changed

Diff for: css2json-loader.js

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
const parse = require("tns-core-modules/css").parse;
2+
const nl = "\n";
3+
4+
module.exports = function(content) {
5+
const ast = parse(content);
6+
const dependencies = getImportsFrom(ast)
7+
.map(mapURI)
8+
.reduce((dependencies, {uri, requireURI}) =>
9+
dependencies + `global.registerModule(${uri}, () => require(${requireURI}));${nl}`, "");
10+
11+
const str = JSON.stringify(ast, (k, v) => k === "position" ? undefined : v);
12+
return `${dependencies}module.exports = ${str};`;
13+
}
14+
15+
function getImportsFrom(ast) {
16+
if (!ast || ast.type !== "stylesheet" || !ast.stylesheet) {
17+
return [];
18+
}
19+
return ast.stylesheet.rules
20+
.filter(rule => rule.type === "import")
21+
.map(importRule => importRule.import.replace(/[\'\"]/gm, ""));
22+
}
23+
24+
function mapURI(uri) {
25+
return {
26+
uri: JSON.stringify(uri),
27+
requireURI: JSON.stringify(uri[0] === "~" && uri[1] !== "/" ? uri.substr(1) : uri)
28+
};
29+
}

Diff for: package.json

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"generate-android-snapshot": "./bin/generate-android-snapshot"
2727
},
2828
"dependencies": {
29+
"minimatch": "^3.0.4",
2930
"semver": "^5.4.1",
3031
"shelljs": "^0.6.0"
3132
},

Diff for: plugins/PlatformFSPlugin.js

+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
const { parse: parseFile, join, basename, relative } = require("path");
2+
const minimatch = require("minimatch");
3+
4+
function PlatformFSPlugin({platform, platforms, ignore}) {
5+
this.platform = platform;
6+
this.platforms = platforms;
7+
this.ignore = ignore || [];
8+
9+
const alienPlatforms = this.platforms.filter(p => p !== platform);
10+
const alienPlatformFilters = alienPlatforms.map(platform => ({
11+
endsWithSuffix: `.${platform}`,
12+
contains: `.${platform}.`
13+
})).map(({endsWithSuffix, contains}) => baseFileName =>
14+
baseFileName.endsWith(endsWithSuffix) ||
15+
baseFileName.indexOf(contains) != -1);
16+
this.isNotAlienPlatformFile = file => !alienPlatformFilters.some(filter => filter(basename(file)));
17+
18+
const currentPlatformExt = `.${platform}`;
19+
this.trimPlatformSuffix = file => {
20+
const {dir, name, ext} = parseFile(file);
21+
if (ext === currentPlatformExt) {
22+
return join(dir, name);
23+
} else if (name.endsWith(currentPlatformExt)) {
24+
return join(dir, name.substr(0, name.length - currentPlatformExt.length) + ext);
25+
}
26+
return file;
27+
}
28+
}
29+
30+
PlatformFSPlugin.prototype.apply = function(compiler) {
31+
const context = this.context = compiler.context;
32+
const minimatchFileFilters = this.ignore.map(pattern => {
33+
const minimatchFilter = minimatch.filter(pattern);
34+
return file => minimatchFilter(relative(context, file));
35+
});
36+
37+
this.isIgnored = file => minimatchFileFilters.some(filter => filter(file));
38+
39+
compiler.inputFileSystem = this.mapFileSystem(compiler.inputFileSystem);
40+
}
41+
42+
PlatformFSPlugin.prototype.mapFileSystem = function(fs) {
43+
const platform = this.platform;
44+
const platforms = this.platforms;
45+
const alienPlatforms = this.alienPlatforms;
46+
const isNotAlienPlatformFile = this.isNotAlienPlatformFile;
47+
const trimPlatformSuffix = this.trimPlatformSuffix;
48+
const isIgnored = this.isIgnored;
49+
const isNotIgnored = file => !isIgnored(file);
50+
51+
const mappedFS = {
52+
get _statStorage() { return fs._statStorage; },
53+
get _readFileStorage() { return fs._readFileStorage; },
54+
get _readdirStorage() { return fs._readdirStorage; }
55+
};
56+
57+
["readFile", "provide", "stat", "readJson", "readlink"].forEach(mapPath);
58+
["readdir"].forEach(filterResultingFiles);
59+
60+
return mappedFS;
61+
62+
/**
63+
* For FS functions that get as first argument a file path,
64+
* this will map it to a platform specific file if such file exists or fallback to the default.
65+
* Also the last argument must be a function that handles results such as (err, files[]),
66+
* it will invoke err for files that are ignored.
67+
*/
68+
function mapPath(name) {
69+
const base = fs[name];
70+
mappedFS[name] = function() {
71+
const originalFilePath = arguments[0];
72+
const callback = arguments[arguments.length - 1];
73+
if (isIgnored(originalFilePath)) {
74+
callback(new Error("File " + originalFilePath + " is ignored!"));
75+
return;
76+
}
77+
const {dir, name, ext} = parseFile(originalFilePath);
78+
const platformFilePath = join(dir, name + ("." + platform) + ext);
79+
fs.stat(platformFilePath, (err, stat) => {
80+
if (!err && stat && stat.isFile()) {
81+
arguments[0] = platformFilePath;
82+
}
83+
base.apply(fs, arguments);
84+
});
85+
}
86+
}
87+
88+
/**
89+
* For FS functions that get as a last argument a function,
90+
* that handles results such as (err, files[]),
91+
* will filter and map the returned files[].
92+
*/
93+
function filterResultingFiles(name) {
94+
const base = fs[name];
95+
mappedFS[name] = function() {
96+
const callback = arguments[arguments.length - 1];
97+
const dir = arguments[0];
98+
if (isIgnored(dir)) {
99+
// Return empty file list for filtered directories.
100+
callback(null, []);
101+
return;
102+
}
103+
arguments[arguments.length - 1] = function(err, files) {
104+
if (err) {
105+
callback(err);
106+
} else {
107+
// Create absolute paths for "ignored" testing, map platforms, and return back the base name.
108+
const result = files
109+
.map(file => join(dir, file))
110+
.filter(isNotIgnored)
111+
.filter(isNotAlienPlatformFile)
112+
.map(trimPlatformSuffix)
113+
.map(file => basename(file));
114+
115+
// app.css and app.android.css will both map into app.css and we remove duplicates:
116+
const uniqueResults = [...new Set(result)];
117+
callback(null, uniqueResults);
118+
}
119+
}
120+
base.apply(fs, arguments);
121+
}
122+
}
123+
}
124+
125+
exports.PlatformFSPlugin = PlatformFSPlugin;

Diff for: plugins/PlatformSuffixPlugin.js

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
const parseFile = require("path").parse;
2+
3+
function PlatformSuffixPlugin(platform, platforms) {
4+
this.platform = platform;
5+
this.platforms = platforms || ["ios", "android"];
6+
}
7+
exports.PlatformSuffixPlugin = PlatformSuffixPlugin;
8+
9+
PlatformSuffixPlugin.prototype.apply = function(resolver) {
10+
var platform = this.platform;
11+
var platforms = this.platforms;
12+
13+
resolver.plugin("file", function(request, callback) {
14+
const fs = this.fileSystem;
15+
const file = this.join(request.path, request.request);
16+
const query = request.query;
17+
const pFile = parseFile(file);
18+
const platformFile = this.join(pFile.dir, pFile.name + ("." + platform) + pFile.ext);
19+
fs.stat(platformFile, (err, stat) => {
20+
if (!err && stat && stat.isFile()) {
21+
const err = undefined;
22+
const path = platformFile;
23+
callback(err, { file: true, path, query });
24+
} else {
25+
fs.stat(file, (err, stat) => {
26+
if (!err && stat && stat.isFile()) {
27+
const err = undefined;
28+
const path = file;
29+
callback(err, { file: true, path, query });
30+
} else {
31+
callback();
32+
}
33+
});
34+
}
35+
});
36+
});
37+
}

Diff for: plugins/index.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
module.exports = Object.assign({},
22
require("./GenerateBundleStarterPlugin"),
33
require("./NativeScriptJsonpPlugin"),
4-
require("./NativeScriptSnapshotPlugin")
4+
require("./NativeScriptSnapshotPlugin"),
5+
require("./PlatformSuffixPlugin"),
6+
require("./PlatformFSPlugin"),
57
);

0 commit comments

Comments
 (0)