Skip to content

Copy node_modules to platform on prepare (fix Node6/npm 3.x bug) #2152

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 26, 2016

Conversation

petekanev
Copy link
Contributor

@petekanev petekanev commented Oct 21, 2016

Changes in this PR will allow copying of all production (non-dev) node_modules into the platform's tns_modules independent of whether they have been installed using tns install (npm 2.x) or npm install with npm version 3+.

The proposed implementation however will iterate through the production modules' package.jsons on every build. Traversal of all package's production dependencies is also a bit slower than before. I'll look into a way to address only changes on subsequent builds, and potentially optimize traversals.

Issue #2149 and the respective PR will most likely address preparing of modules on each rebuild. Has yet to be verified.

There are currently 3 failing tests testing how and where npm_modules are copied into the platforms dir.

The changes have been tested with the Groceries app (master branch) with both tns install (npm 2), and npm install (npm 3)

Addresses issues: #1989, #1845, #1740

Also invalidates PR #2136

Depends on changes in both runtimes: NativeScript/android#587, NativeScript/ios-jsc#673;

ping: @rosen-vladimirov @hdeshev @Plamen5kov @atanasovg

@petekanev petekanev added this to the 2.4.0 milestone Oct 21, 2016
copy production node_modules as-is to the project's tns_modules
Copy link
Contributor

@hdeshev hdeshev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have two concerns with this PR:

  1. The getProductionDependencies method is too complex and needs to be broken up in small chunks, cleaned up, and possibly moved to a different class.
  2. I do not see anything preventing pulling two versions of a package (think huge packages like Angular) to your app's tns_modules dir, if it has been requested by your app's dependencies. What should we do in that case?

// console.log(productionDependencies);

// TODO: Pip3r4o - result is not used currently
// let nodeModules = this.getChangedNodeModules(absoluteOutputPath, platform, lastModifiedTime).wait();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we get rid of commented out code?

var deps: any = [];
var seen: any = {};

var pJson = path.join(projectPath, "package.json");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, avoid weird abbreviations like pJson. How about packageJsonPath?


}
// module is scoped
else {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

} else {

var dep = addDependency(deps, name, scopedModulePath, 0);
traverseModule(scopedModulePath, depth, dep);
}
else {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

} else {

return;
}

var dep = addDependency(deps, name, modulePath, 0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, please avoid abbreviations: dep


return filterUniqueDirectories(deps);

function traverseChild(name: string, currentModulePath: string, depth: number) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those inner functions are very confusing and unreadable. Consider moving them to methods of this class or an entirely new class.

}
}

function traverseModule(modulePath: string, depth: number, currentDependency: any) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the difference when traversing a "child" and a "module". I think one of these functions needs a better name.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also please add return type to the function

}
}

function filterUniqueDirectories(dependencies: any) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use a map keyed by the dependency directory here (simulating a set collection) instead of doing linear traversals all the time?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noted. I used a string map initially to check if the plugin's already present in the list, but that approach omitted corner cases where 2 plugins with different versions were present. I'll revisit it again

@petekanev
Copy link
Contributor Author

@hdeshev about 2. - currently we will pull both versions of the package, as extracted by npm, you can't tell by version alone if any of the packages' dependents are compatible with all, or just 1 of said package's version.
Previously only the package with the greatest version will be pulled from node_modules, which is not always desired.

let pJson = path.join(projectPath, "package.json");
let nodeModulesDir = path.join(projectPath, "node_modules");

let content = require(pJson);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would advise using $fs as this will make the code more testable:

this.$fs.readJson(pJson).wait();


let content = require(pJson);

Object.keys(content.dependencies).forEach((key) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if there's no dependencies section in the package.json, this code will fail. I would use lodash's _.each:

_.keys(content.dependencies)
   .forEach(key => { 
    ....
    });

}

if (packageJsonContents.dependencies) {
Object.keys(packageJsonContents.dependencies).forEach((key) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above - it's safer to use:

_.keys(packageJsonContent.dependencies).forEach(...

}
}

function addDependency(deps: any[], name: string, directory: string, depth: number) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this function needs return type

}
}

function traverseModule(modulePath: string, depth: number, currentDependency: any) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also please add return type to the function

}

function addDependency(deps: any[], name: string, directory: string, depth: number) {
let dep: any = {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can use shorter syntax here:

let dep = {
    name,
    directory,
    depth
};

function filterUniqueDirectories(dependencies: any) {
let unique: any = [];
let distinct: any = [];
for (let i in dependencies) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can get directly the dependency by using:

for (let dep of dependencies) {
   if (distinct.indexOf(dep.directory) > -1) {
....
}

public copyModules(dependencies: IDictionary<ILocalDependencyData>, platform: string): void {
_.each(dependencies, dependency => {
public copyModules(dependencies: any[], platform: string): void {
for (var entry in dependencies) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var -> let

_.each(dependencies, dependency => {
public copyModules(dependencies: any[], platform: string): void {
for (var entry in dependencies) {
var dependency = dependencies[entry];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var -> let


shelljs.mkdir("-p", targetDir);
shelljs.cp("-Rf", dependency.directory, targetDir);
// console.log("Copied dependency: " + dependency.name);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you remove the commented line or add it as log trace message:

this.$logger.trace(`Copied dependency: ${dependency.name}`);

@petekanev petekanev force-pushed the pete/copy-node_modules branch 2 times, most recently from b66b4a7 to cbfd5b8 Compare October 24, 2016 11:56
@petekanev
Copy link
Contributor Author

@hdeshev @rosen-vladimirov addressed concerns with style and readability

return dependency;
}

private ensureModuleExists(modulePath: string): boolean {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rename this to moduleExists. Nothing to ensure anyway.

// check if key appears in a scoped module dependency
let isScoped = name.indexOf('@') === 0;

if (!isScoped) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The scoped/unscoped branches seem to be mostly the same code copied/pasted over. Can we eliminate the duplication in a shared method and pass the different paths as parameters?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right! I seem to have missed it!

@petekanev petekanev force-pushed the pete/copy-node_modules branch 2 times, most recently from f4a99cd to 1b80824 Compare October 25, 2016 06:44
@petekanev petekanev force-pushed the pete/copy-node_modules branch from 1b80824 to 7b10417 Compare October 25, 2016 08:54
@Plamen5kov
Copy link
Contributor

👍 looks good, the sooner this is in master, the better

@petekanev petekanev merged commit 0bc09ec into master Oct 26, 2016
rosen-vladimirov added a commit that referenced this pull request Apr 13, 2017
A while ago, we had a logic to find out which node_modules should be processed. However it is not used anymore (removed in #2152 ).
So remove the code - we do not need it anymore.
rosen-vladimirov added a commit that referenced this pull request Apr 13, 2017
* Fix Android debugging

When executing `tns debug android` for slow emulators/devices we have logic to wait several seconds until debugger is attached.
However we've missed to `await` the `sleep` method and in fact we are not waiting at all. Add the missing await, which fixes the `tns debug android` command.

* Remove unused code

A while ago, we had a logic to find out which node_modules should be processed. However it is not used anymore (removed in #2152 ).
So remove the code - we do not need it anymore.

* Fix type
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants