Skip to content
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

feat(build): add support for external modules #814

Merged
merged 1 commit into from
Mar 3, 2018
Merged

feat(build): add support for external modules #814

merged 1 commit into from
Mar 3, 2018

Conversation

DarkHarlock
Copy link
Contributor

Now you can inject stubs for modules that are only available at runtime and avoid File not found or not accessible console logs via aurelia.json or cli task.

Closes #802

Now you can inject stubs for modules that are only available at runtime and avoid `File not found or not accessible` console logs via `aurelia.json` or cli task.

Closes #802
@DarkHarlock
Copy link
Contributor Author

This is the code I've done to solve my issue. It's pretty minimal but allow hooks for modules in two ways.
The first one is with a simple add in the aurelia.json

{
  "name": "cli-test",
  "build": {
    "loader": {
      "externalModules": {
        "my-awesome-module": ["infrastructure/service1", "other"]
      }

The second one is more powerful because can intercept the requested module and inject dynamic content. To do this you have to edit the build.ts or build.js and change the readProjectConfiguration implementation in this way:

function fixModule(defaultRead, id, filePath) {
  if (/* some custom logic */) {
    return `
define([${/* some dynamic dependency */}], function() {});    
`;
   }
}
function  readProjectConfiguration() {
  project.build.loader.externalModules["other"] = fixModule;
  return buildCLI.src(project);
}

If the provided function returns a falsy value the normal workflow is done, otherwise is used as content.
At this time, I'm using this with success changing the behavior between build server and development workstation throu a simple exclusion file not under git.

@JeroenVinke
Copy link
Collaborator

great work!

@JeroenVinke JeroenVinke merged commit cb245cd into aurelia:master Mar 3, 2018
@Alexander-Taran
Copy link
Contributor

@DarkHarlock can you please provide another PR to document this as well?

@DarkHarlock
Copy link
Contributor Author

@Alexander-Taran Yes, I can provide it in the next future. I only need to know where.

@DarkHarlock DarkHarlock deleted the external_modules branch March 3, 2018 20:22
@3cp
Copy link
Member

3cp commented Aug 8, 2018

Just found this feature when I am cleaning up #862. This is currently not supported by auto-tracing, but would not be too troublesome to add.

Before working on it, I want to ask few questions to clarify my understanding.

For feature1,

"externalModules": {
        "my-awesome-module": ["infrastructure/service1", "other"]
      }

Why not just define src/my-awesome-module.js as

import "infrastructure/service1";
import "other";

For feature2, a build-time callback for inject a module, may I know what's your use case for this? If you want runtime behaviour depending on your build env, you could add more flag to aurelia_project/environments/*.js and check the flag (like environment.testing in main.js) to conditionally run different logic. This is what cli bootstrapped app provided, and it is a clean way to conditionally control runtime behaviour.

@JeroenVinke mentioned electron in #802. I don't know electron much, what's the related use case there?

The reason I ask the above questions, is that dynamic injection of modules makes source code very tightly coupled with cli bundler, which I personally feel should be avoid. For example, if one wants to test the source code without using bundler, I mean using tape + babel-register to directly test in nodejs env for logic layer (not ui layer), we cannot because those missing "import" would not work.

@DarkHarlock
Copy link
Contributor Author

This feature was basically developed to avoid the "File not found" error in the console when a module is imported but not available on filesystem. This happen when you have to require a module that is only available at runtime.

The solution you provide:

Why not just define src/my-awesome-module.js as

import "infrastructure/service1";
import "other";

is not feasible because:

  1. for the loader the my-awesome-module.js must exists on filesystem, but it is available at runtime, not at build time. This is a plugin customization that is put into the application during the deploy and is imported with a import * as awesomeplugin from "my-awesome-module" somewhere in the project.

  2. I need a way to declare in the project the plugin dependency (just as a stub) to ensure that are built and packaged (the application can not import this dependencies, it does not known it). At this time I inject externalModules by a javascript task that imports declarations from a sort of plugin repository metadata. A plugin can also have plugin dependencies, and this makes it more complicated to manage.

  3. If you use electron, you are in a node.js environment and so you can reference anywhere modules like fs, path, stream, etc... that are built-in. With this hack you can declare fs, and it will not report issue during build but still work on runtime (this require a little other hack that translate amd require into commonjs require, but this is another story).

@DarkHarlock
Copy link
Contributor Author

Just some other infos:

  1. I call it plugin but it is more likely a customized module (it contains custom behaviors, logics and UI for different deploys and customers)

  2. This kind of module must not be bundled, and the require for my-awesome-module must not resolve to anything in the bundle so the loader can fallback to a classic http call to the server and download it.

@3cp
Copy link
Member

3cp commented Aug 8, 2018

Thx! I kind of get some idea what you are doing. Will contact you on gitter for more details.

@3cp
Copy link
Member

3cp commented Aug 27, 2018

FYI, after discussion with @DarkHarlock, we are going to change the implementation for supporting external package in auto-tracing. Since the current feature is undocumented, we are not going to call it breaking change :-)

Instead of using aurelia.json, it's going to be a flexible optional API on build.dest(). This will be documented along with releasing auto-tracing.

Usage in aurelia_project/tasks/build.js

function writeBundles() {
  return buildCLI.dest({
    onRequiringModule: function(moduleId) {
         //...
    }
  });
}
feat: support onRequiringModule(moduleId) callback
onRequiringModule callback is called before auto-tracing on a moduleId.
It would not be called for any modules provided by app's src files or explicit
dependencies config in aurelia.json.

Three types possible result (all can be returned in promise):
1. Boolean false: ignore this moduleId;
2. Array of strings like ['a', 'b']: require module id "a" and "b" instead;
3. A string: the full JavaScript content of this module
4. All other returns are ignored and go onto performing auto-tracing.

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