Skip to content

Commit

Permalink
feat(ios): include project root node_modules folder in app
Browse files Browse the repository at this point in the history
  • Loading branch information
sgtcoolguy committed Mar 1, 2021
1 parent 0fd053b commit 84d5641
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 93 deletions.
2 changes: 1 addition & 1 deletion build/lib/packager.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ const packageJSON = require('../../package.json');
const utils = require('./utils');
const copyFile = utils.copyFile;
const copyFiles = utils.copyFiles;
const moduleCopier = require('./module-copier');

const ROOT_DIR = path.join(__dirname, '../..');
const TMP_DIR = path.join(ROOT_DIR, 'dist', 'tmp');
Expand Down Expand Up @@ -134,6 +133,7 @@ class Packager {
*/
async packageNodeModules() {
console.log('Copying production npm dependencies');
const moduleCopier = require('../../cli/lib/module-copier');
// Copy node_modules/
await moduleCopier.execute(this.srcDir, this.zipSDKDir);

Expand Down
32 changes: 24 additions & 8 deletions build/lib/module-copier.js → cli/lib/module-copier.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
'use strict';

const copier = {};
module.exports = copier;

const fs = require('fs-extra');
const path = require('path');

const NODE_MODULES = 'node_modules';

const copier = {};
/**
* @param {string} projectPath absolute filepath for project root directory
* @param {object} [options] options object
* @param {boolean} [options.includeOptional=true] whether to include optional dependencies when gathering
* @returns {Promise<Set<string>>} A Promise that resolves on completion
*/
copier.gather = async function (projectPath, options = { includeOptional: true }) {
if (projectPath === null || projectPath === undefined) {
throw new Error('projectPath must be defined.');
}
// resolve path names for file copying
projectPath = path.resolve(projectPath);

// recursively gather the full set of dependencies/directories we need to copy
const root = new Dependency(null, 'fake-id', projectPath);
const directoriesToBeCopied = await root.getDirectoriesToCopy(options.includeOptional);

return new Set(directoriesToBeCopied); // de-duplicate
};

/**
* @param {string} projectPath absolute filepath for project root directory
* @param {string} targetPath absolute filepath for target directory to copy node_modules into
Expand All @@ -27,11 +45,7 @@ copier.execute = async (projectPath, targetPath, options = { includeOptional: tr
projectPath = path.resolve(projectPath);
targetPath = path.resolve(targetPath);

// recursively gather the full set of dependencies/directories we need to copy
const root = new Dependency(null, 'fake-id', projectPath);
const directoriesToBeCopied = await root.getDirectoriesToCopy(options.includeOptional);

const dirSet = new Set(directoriesToBeCopied); // de-duplicate
const dirSet = await copier.gather(projectPath, options);
// back to Array so we can #map()
const deDuplicated = Array.from(dirSet);

Expand Down Expand Up @@ -111,3 +125,5 @@ class Dependency {
return null;
}
}

module.exports = copier;
102 changes: 61 additions & 41 deletions iphone/cli/commands/_build.js
Original file line number Diff line number Diff line change
Expand Up @@ -5025,35 +5025,47 @@ iOSBuilder.prototype.writeDebugProfilePlists = function writeDebugProfilePlists(

iOSBuilder.prototype.gatherResources = async function gatherResources() {
console.time('gathering resources');
const Walker = require('../lib/gather');
const Result = Walker.Result;
const walker = new Walker({
tiappIcon: this.tiapp.icon,
useAppThinning: this.useAppThinning,
const gather = require('../lib/gather');
const walker = new gather.Walker({
ignoreDirs: this.ignoreDirs,
ignoreFiles: this.ignoreFiles,
});

this.logger.info(__('Analyzing Resources directory'));
const firstWave = await Promise.all([
walker.walk(path.join(this.titaniumSdkPath, 'common', 'Resources', 'ios'), this.xcodeAppDir),
walker.walk(path.join(this.projectDir, 'node_modules'), path.join(this.xcodeAppDir, 'node_modules')),
walker.walk(path.join(this.projectDir, 'platform', 'iphone'), this.buildDir),
walker.walk(path.join(this.projectDir, 'platform', 'ios'), this.buildDir),
walker.walk(path.join(this.projectDir, 'Resources'), this.xcodeAppDir, platformsRegExp),
walker.walk(path.join(this.projectDir, 'Resources', 'iphone'), this.xcodeAppDir),
walker.walk(path.join(this.projectDir, 'Resources', 'ios'), this.xcodeAppDir),
]);
let combined = Result.merge(firstWave);
combined.dontProcessJsFilesReferencedFromHTML();

this.logger.info(__('Analyzing platform files'));
const secondWave = await Promise.all([
walker.walk(path.join(this.projectDir, 'platform', 'iphone'), this.buildDir),
walker.walk(path.join(this.projectDir, 'platform', 'ios'), this.buildDir),
]);
secondWave.unshift(combined); // stick results of our first merge at the front, then merge again
combined = Result.merge(secondWave);
let combined = gather.mergeMaps(firstWave);

const moduleCopier = require('../../../cli/lib/module-copier');
console.log(this.projectDir);
const dirSet = await moduleCopier.gather(this.projectDir);
const nodeModuleDirs = Array.from(dirSet);
const secondWave = await Promise.all(nodeModuleDirs.map(async dir => {
// here dir is the absolute path to the directory
// That means we need to construct the relative path to append to this.projectDir and this.xcodeAppDir
const relativePath = dir.substring(this.projectDir.length + 1);
return walker.walk(dir, path.join(this.xcodeAppDir, relativePath), null, null, relativePath);
}));
// merge the node_modules results on top of the project results... (shouldn't be any conflicts!)
secondWave.unshift(combined);
combined = gather.mergeMaps(secondWave);

this.logger.info(__('Analyzing module files'));
// detect ambiguous modules
this.modules.forEach(module => {
const filename = `${module.id}.js`;
if (combined.has(filename)) {
this.logger.error(__('There is a project resource "%s" that conflicts with a native iOS module', filename));
this.logger.error(__('Please rename the file, then rebuild') + '\n');
process.exit(1);
}
});
// do modules in parallel - and for each we need to merge the results together!
const allModulesResults = await Promise.all(this.modules.map(async module => {
const moduleResults = await Promise.all([
Expand All @@ -5064,55 +5076,63 @@ iOSBuilder.prototype.gatherResources = async function gatherResources() {
walker.walk(path.join(module.modulePath, 'Resources', 'iphone'), this.xcodeAppDir),
walker.walk(path.join(module.modulePath, 'Resources', 'ios'), this.xcodeAppDir),
]);
return Result.merge(moduleResults);
return gather.mergeMaps(moduleResults);
}));
// merge the allModulesResults over top our current combined!
allModulesResults.unshift(combined);
combined = Result.merge(allModulesResults);
combined = gather.mergeMaps(allModulesResults);

this.logger.info(__('Analyzing CommonJS modules'));
const commonJsResults = await Promise.all(this.commonJsModules.map(async module => {
const dest = path.join(this.xcodeAppDir, path.basename(module.id));
// Pass in the relative path prefix we should give because we aren't copying direct to the root here.
// Otherwise index.js in one module "overwrites" index.js in another (because they're at same relative path inside module)
return walker.walk(module.modulePath, dest, /^(apidoc|docs|documentation|example)$/, null, module.id); // TODO Consult some .moduleignore file in the module or something? .npmignore?
}));
// merge the commonJsResults over top our current combined!
commonJsResults.unshift(combined);
combined = gather.mergeMaps(commonJsResults);

// Ok, so we have a Map<string, FileInfo> for the full set of unique relative paths
// now categorize (i.e. lump into buckets of js/css/html/assets/generic resources)
const categorizer = new gather.Categorizer({
tiappIcon: this.tiapp.icon,
useAppThinning: this.useAppThinning,
});
const categorized = await categorizer.run(combined);

this.logger.info(__('Analyzing localized launch images'));
// TODO: this logic is very similar to what we have in gather.Categorizer already! Basically looks in i18n/<lang>/*.png but only inclues those matching LAUNCH_IMAGE_REGEXP
ti.i18n.findLaunchScreens(this.projectDir, this.logger, { ignoreDirs: this.ignoreDirs }).forEach(launchImage => {
const parts = launchImage.split('/'),
file = parts.pop(),
lang = parts.pop(),
relPath = path.join(lang + '.lproj', file);

combined.launchImages.set(relPath, {
categorized.launchImages.set(relPath, {
i18n: lang,
src: launchImage,
dest: path.join(this.xcodeAppDir, relPath),
srcStat: fs.statSync(launchImage)
});
});

// detect ambiguous modules
this.modules.forEach(module => {
const filename = `${module.id}.js`;
if (combined.jsFiles.has(filename)) {
this.logger.error(__('There is a project resource "%s" that conflicts with a native iOS module', filename));
this.logger.error(__('Please rename the file, then rebuild') + '\n');
process.exit(1);
}
});

this.logger.info(__('Analyzing CommonJS modules'));
const commonJsResults = await Promise.all(this.commonJsModules.map(async module => {
const dest = path.join(this.xcodeAppDir, path.basename(module.id));
// Pass in the relative path prefix we should give because we aren't copying direct to the root here.
// Otherwise index.js in one module "overwrites" index.js in another (because they're at same relative path inside module)
return walker.walk(module.modulePath, dest, /^(apidoc|docs|documentation|example)$/, null, module.id); // TODO Consult some .moduleignore file in the module or something? .npmignore?
}));
// merge the commonJsResults over top our current combined!
commonJsResults.unshift(combined);
combined = Result.merge(commonJsResults);

console.timeEnd('gathering resources');
return combined;
return categorized;
};

iOSBuilder.prototype.copyResources = function copyResources(next) {
const unsymlinkableFileRegExp = /^Default.*\.png|.+\.(otf|ttf)$/;

// What we need to do in general terms is:
// - gather together all the input files (this is done via the old "walk" method, now extracted to gatherResources())
// - categorize/filter the input files (currently also done in walk/gatherResources())
// - Then process the categorized files by "bucket":
// - js files to transpile/minify/encyrpt/source maps/copy
// - css files (possibly minify, copy)
// - "resources"/misc (copy)
// - images/assets/launch logos/etc - create asset catalog, image sets, app icons/itunes artwork
// - We also write out some generated files for app properties, require index, environment variables, bootstrap files
util.callbackify(this.gatherResources.bind(this))((err, gatheredResults) => {
if (err) {
return next(err);
Expand Down
Loading

0 comments on commit 84d5641

Please sign in to comment.