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

Commit

Permalink
Add FS, PlatformSuffixPlugin and css2json-loader (#290)
Browse files Browse the repository at this point in the history
  • Loading branch information
PanayotCankov authored Nov 9, 2017
1 parent 0ee667d commit ea29bb6
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 1 deletion.
29 changes: 29 additions & 0 deletions css2json-loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const parse = require("tns-core-modules/css").parse;
const nl = "\n";

module.exports = function(content) {
const ast = parse(content);
const dependencies = getImportsFrom(ast)
.map(mapURI)
.reduce((dependencies, {uri, requireURI}) =>
dependencies + `global.registerModule(${uri}, () => require(${requireURI}));${nl}`, "");

const str = JSON.stringify(ast, (k, v) => k === "position" ? undefined : v);
return `${dependencies}module.exports = ${str};`;
}

function getImportsFrom(ast) {
if (!ast || ast.type !== "stylesheet" || !ast.stylesheet) {
return [];
}
return ast.stylesheet.rules
.filter(rule => rule.type === "import")
.map(importRule => importRule.import.replace(/[\'\"]/gm, ""));
}

function mapURI(uri) {
return {
uri: JSON.stringify(uri),
requireURI: JSON.stringify(uri[0] === "~" && uri[1] !== "/" ? uri.substr(1) : uri)
};
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"generate-android-snapshot": "./bin/generate-android-snapshot"
},
"dependencies": {
"minimatch": "^3.0.4",
"semver": "^5.4.1",
"shelljs": "^0.6.0"
},
Expand Down
125 changes: 125 additions & 0 deletions plugins/PlatformFSPlugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
const { parse: parseFile, join, basename, relative } = require("path");
const minimatch = require("minimatch");

function PlatformFSPlugin({platform, platforms, ignore}) {
this.platform = platform;
this.platforms = platforms;
this.ignore = ignore || [];

const alienPlatforms = this.platforms.filter(p => p !== platform);
const alienPlatformFilters = alienPlatforms.map(platform => ({
endsWithSuffix: `.${platform}`,
contains: `.${platform}.`
})).map(({endsWithSuffix, contains}) => baseFileName =>
baseFileName.endsWith(endsWithSuffix) ||
baseFileName.indexOf(contains) != -1);
this.isNotAlienPlatformFile = file => !alienPlatformFilters.some(filter => filter(basename(file)));

const currentPlatformExt = `.${platform}`;
this.trimPlatformSuffix = file => {
const {dir, name, ext} = parseFile(file);
if (ext === currentPlatformExt) {
return join(dir, name);
} else if (name.endsWith(currentPlatformExt)) {
return join(dir, name.substr(0, name.length - currentPlatformExt.length) + ext);
}
return file;
}
}

PlatformFSPlugin.prototype.apply = function(compiler) {
const context = this.context = compiler.context;
const minimatchFileFilters = this.ignore.map(pattern => {
const minimatchFilter = minimatch.filter(pattern);
return file => minimatchFilter(relative(context, file));
});

this.isIgnored = file => minimatchFileFilters.some(filter => filter(file));

compiler.inputFileSystem = this.mapFileSystem(compiler.inputFileSystem);
}

PlatformFSPlugin.prototype.mapFileSystem = function(fs) {
const platform = this.platform;
const platforms = this.platforms;
const alienPlatforms = this.alienPlatforms;
const isNotAlienPlatformFile = this.isNotAlienPlatformFile;
const trimPlatformSuffix = this.trimPlatformSuffix;
const isIgnored = this.isIgnored;
const isNotIgnored = file => !isIgnored(file);

const mappedFS = {
get _statStorage() { return fs._statStorage; },
get _readFileStorage() { return fs._readFileStorage; },
get _readdirStorage() { return fs._readdirStorage; }
};

["readFile", "provide", "stat", "readJson", "readlink"].forEach(mapPath);
["readdir"].forEach(filterResultingFiles);

return mappedFS;

/**
* For FS functions that get as first argument a file path,
* this will map it to a platform specific file if such file exists or fallback to the default.
* Also the last argument must be a function that handles results such as (err, files[]),
* it will invoke err for files that are ignored.
*/
function mapPath(name) {
const base = fs[name];
mappedFS[name] = function() {
const originalFilePath = arguments[0];
const callback = arguments[arguments.length - 1];
if (isIgnored(originalFilePath)) {
callback(new Error("File " + originalFilePath + " is ignored!"));
return;
}
const {dir, name, ext} = parseFile(originalFilePath);
const platformFilePath = join(dir, name + ("." + platform) + ext);
fs.stat(platformFilePath, (err, stat) => {
if (!err && stat && stat.isFile()) {
arguments[0] = platformFilePath;
}
base.apply(fs, arguments);
});
}
}

/**
* For FS functions that get as a last argument a function,
* that handles results such as (err, files[]),
* will filter and map the returned files[].
*/
function filterResultingFiles(name) {
const base = fs[name];
mappedFS[name] = function() {
const callback = arguments[arguments.length - 1];
const dir = arguments[0];
if (isIgnored(dir)) {
// Return empty file list for filtered directories.
callback(null, []);
return;
}
arguments[arguments.length - 1] = function(err, files) {
if (err) {
callback(err);
} else {
// Create absolute paths for "ignored" testing, map platforms, and return back the base name.
const result = files
.map(file => join(dir, file))
.filter(isNotIgnored)
.filter(isNotAlienPlatformFile)
.map(trimPlatformSuffix)
.map(file => basename(file));

// app.css and app.android.css will both map into app.css and we remove duplicates:
const uniqueResults = [...new Set(result)];
callback(null, uniqueResults);
}
}
base.apply(fs, arguments);
}
}
}

exports.PlatformFSPlugin = PlatformFSPlugin;
37 changes: 37 additions & 0 deletions plugins/PlatformSuffixPlugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const parseFile = require("path").parse;

function PlatformSuffixPlugin(platform, platforms) {
this.platform = platform;
this.platforms = platforms || ["ios", "android"];
}
exports.PlatformSuffixPlugin = PlatformSuffixPlugin;

PlatformSuffixPlugin.prototype.apply = function(resolver) {
var platform = this.platform;
var platforms = this.platforms;

resolver.plugin("file", function(request, callback) {
const fs = this.fileSystem;
const file = this.join(request.path, request.request);
const query = request.query;
const pFile = parseFile(file);
const platformFile = this.join(pFile.dir, pFile.name + ("." + platform) + pFile.ext);
fs.stat(platformFile, (err, stat) => {
if (!err && stat && stat.isFile()) {
const err = undefined;
const path = platformFile;
callback(err, { file: true, path, query });
} else {
fs.stat(file, (err, stat) => {
if (!err && stat && stat.isFile()) {
const err = undefined;
const path = file;
callback(err, { file: true, path, query });
} else {
callback();
}
});
}
});
});
}
4 changes: 3 additions & 1 deletion plugins/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module.exports = Object.assign({},
require("./GenerateBundleStarterPlugin"),
require("./NativeScriptJsonpPlugin"),
require("./NativeScriptSnapshotPlugin")
require("./NativeScriptSnapshotPlugin"),
require("./PlatformSuffixPlugin"),
require("./PlatformFSPlugin"),
);

0 comments on commit ea29bb6

Please sign in to comment.