I have a small EmberJS application that uses Ember-Cli. My application has a private ES6 library that is a bower dependency. Basically, what I want is to import the library and use it wherever I want.
If I'm not wrong, I should transpile the library in my brocfile.js and use it afterwards. Unfortunately, I cannot provide too much concrete information but I'll try my best to be the clearer possible.
My external library is named main-lib and is structured the following way (it is working in another project):
bower_components
main-lib
api.js
main.js
message.js
In the main.js file, I have the following:
import Api from 'main/api';
import Message from 'main/message';
var main = {};
main.api = Api;
main.message = Message;
export default main;
So, what I want to do, is, in my application, to import main and use the different functions it contains.
Example, in a certain emberjs controller:
import Main from 'main';
//use Main here
To do so, I thought of doing the following in my brocfile.js
var tree = 'bower_components/main-lib';
var ES6Modules = require('broccoli-es6modules');
var amdFiles = new ES6Modules(tree, {
format: 'amd',
bundleOptions: {
entry: 'main.js',
name: 'mainLib'
}
});
However, this does nothing. Basically, I want the transpiled files to be included in vendor.js or somewhere where I would be able to use the library by importing it.
There's something I'm missing here but I can't pinpoint it.
Edit1: After adding these lines at the end of my brocfile.js:
mergeTrees = require('broccoli-merge-trees')
module.exports = mergeTrees([app.toTree(), amdFiles]);
I can get an ES5 that looks like this:
define(['main/api', 'main/message'], function(api, message) {
var main = {};
main.api = Api;
main.message = Message;
var _main = main;
return _main;
});
The problem is that it does not import main/api and main/message as well. Do I have to repeat the code for each file that I want ?
Also, the file is not concatenated in vendor.js but simply but at the root of /dist
You have the following: import Api from 'main/api' - but I don't see a folder called main in what you've explained - only a folder called main-lib ...
Could it be that main/api and main/message are not included because they actually don't exist? You might need to use main-lib/api and main-lib/message in your main.js file
The integration of Broccoli with ember-cli already includes a transpiler, so I think something like this should be enough:
app.import('bower_components/main-lib/main.js', {
type: 'vendor',
exports: { 'main': ['default'] }
);
And then you can:
import Main from 'main';
With what you currently have in your Brocfile you still need to merge your amdFiles (app.import would do that for you).
Something like:
mergeTrees = require('broccoli-merge-trees')
module.exports = mergeTrees([app.toTree(), amdFiles]);
None of this is tested, but you get the idea.
Ember seem to be advocating using: https://www.npmjs.com/package/broccoli-es6modules
This would mean importing your module would look something like:
var mergeTrees = require('broccoli-merge-trees');
var tree = './bower_components/main-lib';
var ES6Modules = require('broccoli-es6modules');
var amdFiles = new ES6Modules(tree, {
format: 'amd',
bundleOptions: {
entry: 'main.js',
name: 'main-lib'
}
});
module.exports = mergeTrees([app.toTree(), amdFiles])
Related
I want to create an object that would import functions from another folder and it would look something like this:
class = {
functions: {
//All functions here
}
}
The functions would be inside of a different folder, however, I want to make some sort of importer in which it would make new classes for each new function/file it finds inside of the folder.
someFunction.js Function File:
function someFunction() {
console.log("this is some function");
}
So I would like for something to look like this:
class.functions.someFunction()
No, I do not want to have it hard coded into the object, I want to import all functions from a folder and create functions like that.
Well, first I wan't to answer your question as I think you want, even if I also think it is not the correct way to proceed.
I'll also assume that with class you are not referring to an actual ES6 Class, but we are talking about a plain object.
So this is the code:
const fs = require('fs');
const path = require('path');
function importer(dirPath) {
const absoluteDirPath = path.normalize(
path.isAbsolute(dirPath)
? dirPath
: path.resolve(process.cwd(), dirPath)
);
const output = {
functions: {}
};
const content = fs.readdirSync(path.normalize(absoluteDirPath));
content.forEach((basename) => {
const absoluteItemPath = path.join(absoluteDirPath, basename);
if (fs.statSync(absoluteItemPath).isFile() && /\.js$/i.test(basename)) {
output.functions[basename.slice(-3)] = require(path.relative(
__dirname,
absoluteItemPath
));
}
});
return output;
}
module.exports = importer;
For this to work, all your functions in your files should be exported like:
module.exports = function myFunction() {};
To use the 'importer', you just do:
const artemis = importer('/path/to/directory'); // PATH MUST BE ABSOLUTE OR RELATIVE TO CWD.
/*
SUPPOSING THAT YOUR DIRECTORY CONTAINS THE FOLLOWING FILES:
function1.js
function2.js
Then you can do:
artemis.function1();
artemis.function2();
Please note that your files must be named in a JS friendly way (a valid string for an object key).
*/
A final important note about this odd method: This will only ever work in a NodeJS environment. Even if functions could have worked in other environments (like a browser). The next method, will work for any ECMAScript environment after proper building process: transpilation (EX: Babel) and bundling (EX: Webpack).
Suggested Solution
Use ES6 Static import / export like modern JS libraries do. This comes with huge benefits, from static code analysis to tree shaking and more.
Let's suppose the following hierarchy:
// - index.js
// - internals/
// - index.js
// - module-1.js
// - module-2.js
internals/module-1.js
function module1() {}
export {module1};
internals/module-2.js
import {module1} from 'module-1.js';
function module2() {
// YOU CAN USE module1 IF YOU NEED. (AVOID CIRCULAR REFERENCES)
module1();
}
export {module2};
internals/index.js
import {module1} from './module-1.js';
import {module2} from './module-2.js';
export {module1, module2};
index.js
import * as moduleGroup from './internals/index.js';
export {moduleGroup};
Finally, where you import your moduleGroup, you can do:
moduleGroup.module1();
moduleGroup.module2();
Obviously this is a basic scenario, but this is, IMHO, the correct way to deliver a group of functions and other stuff. Please let me know if you have any doubt.
The code environment is browser. bundle tool is webpack. I have a router.js file like:
import foo from './views/foo.vue'
import bar from './views/bar.vue'
import zoo from './views/zoo.vue'
//use foo, bar, zoo variables
I've many '.vue' files to import like this under views folder. Is there a programmatical way to auto import all [name].vue as local variable [name]? So when I add or remove a vue file in views, I don't need to manually edit router.js file. this one seems a little dirty.
for (let name of ['foo', 'bar', 'zoo']) {
global[name] = require(`./views/${name}.vue`)
}
Nope, that's it. You have a choice between dynamic import and automation, or explicit coding and type-checking / linting.
Unfortunately, it's one or the other. The only other way to do it is meta-programming, where you write code to write your code.
So you generate the import statements in a loop like that, and write the string into the source file, and use delimiting comment blocks in the source file to identify and update it.
The following works for me with webpack and vue.
I actually use it for vuex and namespaces. Hope it helps you as well.
// imports all .vue files from the views folder (first parameter is the path to your views)
const requireModule = require.context('./views', false, /\.vue$/);
// create empty modules object
const modules = {};
// travers through your imports
requireModule.keys().forEach(item => {
// replace extension with nothing
const moduleName = item.replace(/(\.\/|\.vue)/g, '');
// add item to modules object
modules[moduleName] = requireModule(item).default;
});
//export modules object
export default modules;
We have a legacy Backbone application that we are moving over to React. An interim step we are attempting is loading a bundled Backbone module into a React page, until we have time to fully rewrite it. I am halfway there, I can bundle up the app and all its dependencies with r.js using a config like this:
({
...
baseUrl: './',
name: 'myapp',
paths: {
'myapp': './legacy/app'
},
out: 'src/appbuilt.js'
...
})
The module is set up like this:
define(function(require) {
var $ = require('jquery'),
_ = require('underscore'),
...
templates = $(require('text!templates/app.html')),
app = {};
app.View = .....
app.Model = .....
return app;
});
That bundle works on the Backbone side. Next I need to turn that into something I can import into React and render. I am trying things like this:
npx babel src/appbuilt.js --out-file src/appbuilt.es6.js --plugins=#babel/transform-modules-umd
Which works to give me a UMD module, but importing it like this:
import * as legacyapp from '../../appbuilt.es6';
Gives me warnings on the build like:
Critical dependency: require function is used in a way in which dependencies cannot be statically extracted
And errors on page load that are probably symptoms of something:
Uncaught TypeError: name.split is not a function
What is the secret sauce to get my module converted into something I can use? I am open to modifying how the Backbone app does its imports, or making a wrapper of some kind that is more easily translatable.
I am not sure but I can guess that the format of your AMD modules is the problem here. Try converting them to regular AMD modules:
define(
['jquery', 'underscore', 'text!templates/app.html' /* add rest of dependencies here */],
function ($, underscore, templates /** add parameters for rest of dependencies here */)
{
var app = {};
// app.View = ...
// app.Model = ...
return app;
}
);
I have an existing package written in JavaScript that I'm trying to convert to TypeScript. My question is, what is the equivalent of module.exports for ES6 Modules?
For example if I have the following code:
module.exports = function () {
console.log("Hello World");
};
The only way I have found to do this in ES Modules is by doing something like the following.
const myFunc = function () {
console.log("Hello World");
};
export default myFunc;
// OR
export { myFunc };
Of course, this is not the same thing as module.exports = //....
Which leads to this problem. When I run this through TypeScript and get the outputted code, users of the package will either need to use an import defaultExport from "module-name"; statement (which isn't bad), or instead of being able to access it using require("module-name") they will have to use require("module-name").default (if they are using standard JS, not TypeScript). Which obviously is a breaking API change for standard JavaScript users.
Obviously, the goal here is to have no breaking API changes (and require no changes for customers code).
One more requirement to any solution. In other places in the package I need to be able to export a lot of different files as one object. So in the past I've been doing:
module.exports = {
"thing1": require("./a.js"),
"thing2": require("./b.js")
};
So I need to be able to export this as normal without requiring the user to do thing1.default. This one so far seems like the easier requirement since I'm exporting an object which ES Modules seems to handle really well. It's when you are exporting a singular (non-named) expression that it gets really tricky.
Is there a way to do this without having to make this a breaking change to the API?
The solution here is to use the TypeScript export = command. This allows you to use either require or import with the module.
export = function () {
console.log("Hello World");
};
import myModule from "./lib/myModule";
// OR
const myModule = require("./lib/myModule");
For more information the TypeScript documentation has a guide about Migrating from JavaScript, and specifically a section called Exporting from Modules.
Typescript recommends not to rewrite your code, but instead to configure it to allow js modules.
When we were working on converting a very large app from js to TS, we took the position of writing new code in TS, and slowly converting js into ts (but not spend a whole lot of time just converting).
We needed to enable few options in the tsconfig.json, such as :
{
...,
"compilerOptions" : {
...
"moduleResolution": "node",
"allowJs": true,
...
},
"include" [
"path/to/my/js",
]
}
you also need to update your webpack.config.js:
module.exports = {
// ... my settings
resolve: {
// Add '.ts' and '.tsx' as resolvable extensions.
extensions: ["", ".webpack.js", ".web.js", ".ts", ".tsx", ".js"]
},
}
You might also have to declare require as a function (if you are getting errors such as require is undefined):
declare function require(path: string): any;
more info is available here :
https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html
Most accurate replacement for module.exports in typescript is export default
export default {
"thing1": require("./a.js"),
"thing2": require("./b.js")
}
then you can use them like below
import myThings from '.....'
Running a node.js app provides a means to load an included js script as a class/variable.
--- app.js ---
var mine = require('myClass');
mine.DoSomething();
How does node know that MyClass is the file "js/MyClassFile.js"?
What is the HTML <script> equivalent?
It use something called modules.
For example in js/MyClassFile.js there must be something like
exports.myClass = function (r) {
return {DoSomething: function(){} };
};
What is the HTML equivalent?
If by html, you mean browser, then there is options like browserify, systemjs, requirejs, etc, etc
for more info check Writing Modular JavaScript With AMD, CommonJS & ES Harmony by Addy Osmani out.
When you require a file in say app.js, you should use its relative path and export it using module.exports. That's how Node.js knows where to look for it.
--- app.js ---
var mine = require('../myClass')
mine.doSomething();
--- myClass.js ---
var myClass = {
}
module.exports = myClass;
How does node know that MyClass is the file "js/MyClassFile.js"?
Node decides it on basis of relative path so if you are in js folder and try to use var mine = require('myClass'); then it means then myClass is in js folder
for html equivalent you need to use modules but you can do it in es6 like this, please note es6 support is still limited
// lib/math.js
export function sum (x, y) { return x + y }
export var pi = 3.141593
// someApp.js
import * as math from "lib/math"
console.log("2π = " + math.sum(math.pi, math.pi))
// otherApp.js
import { sum, pi } from "lib/math"
console.log("2π = " + sum(pi, pi))
otherwise you can look at this How do I include a JavaScript file in another JavaScript file?