I'm taking a crack at changing my current RequireJS workflow to a Browserify + Watchify one, solely for my frontend Javascript (my backend is Ruby). The only issue I'm running into is that I don't have an easy place to configure what I would called "named paths". Let's assume my frontend is structured like so:
app/
models/
ExampleModel.js
views/
ExampleView.js
main.js
util/
backbone-all.js
vendor/
jquery-2.1.0.js
backbone-1.1.2.js
backbone-marionette-2.0.1.js
underscore-1.6.0.js
In the example above, please note two things:
All of my vendored JS files have their version right in their filename
Note the backbone-all.js file in the util folder
With RequirejS, I could do something like the following:
require.config({
paths: {
"jquery": "vendor/jquery-2.1.0",
"backbone": "vendor/backbone-1.1.2",
"underscore": "vendor/underscore-1.6.0",
"backbone-all": "util/backbone-all"
}
});
And be able to require my code simply by name, rather than fully qualified (or even relative) path. I haven't yet been able to figure out a way to get this to work the exact way that I want on the frontend. The closest I got was to create a dependency map file that is loaded before my application starts (and is globally available), and use it as the keys:
window.d = window.dependency = {
"jquery": "/vendor/jquery-2.1.0",
"backbone": "/vendor/backbone-1.1.2",
"underscore": "/vendor/underscore-1.6.0",
"backbone-all": "/util/backbone-all"
};
var $ = require(d.jquery);
Has anyone encountered this issue, or come across a suitable solution? I came across this post which is similar, but has no accepted answer from 6 months ago. Perhaps things have changed.
You can use the browser field in your package.json to configure a map similar to requireJS's paths map. See the browserify docs.
"browser": {
"jquery": "./vendor/jquery-1.42.js",
"./lib/ops.js": "./browser/opts.js"
}
Related
I wrote a few code that use plain browser Javascript APIs only and can be run well within browser HTML (served by IIS Server or Chrome Extensions). Now I want to contribute to the community by writing a library I have not seen on the market yet. However looking at current solutions, I am at loss at how a project is even built (WebPack/Browserify etc). A side note: I never actually work with NodeJS/NPM before.
For example I have this TypeScript project with the main file AwesomeClass.ts like this:
import { Helper1 } from "./Helper1.js";
import { Helper2 } from "./Helper2.js";
export class AwesomeClass {
doSomething() {
new Helper1().doSomething();
new Helper2().doSomething();
}
}
When built with tsc (I use VS Code as IDE), I can perfectly put this inside an Javascript module and browser can run it.
import { AwesomeClass } from "./AwesomeClass.js";
// Do something with AwesomeClass
So my question is, how do I build and distribute AwesomeClass? Maybe no NPM needed, but from a CDN? Ideally, I think somehow I should have the following output in a dist folder and developer can refer them either by hosting the files by themselves or use a CDN:
awesomeclass.js: For those who want to just use AwesomeClass without module feature (I think it's called UMD?). I.e. expose the AwesomeClass to global scope.
awesomeclass.es6.js: For those who want to use AwesomeClass by using import statement, like import { AwesomeClass } from "https://cdn.example.com/awesomeclass.es6.js";. I like this approach best and want to use this.
I should have something like awesomeclass.d.ts so those using TypeScript can use it. This one is especially tricky because so far I still don't understand how to make it work for 2nd scenario. TypeScript cannot get the type from an import statement from Javascript, and even ignoring that, I cannot get any typing for import statements.
In all cases, I would rather have only one js/ts file packed together if possible but not a deal breaker if I cannot (i.e. user will have to download Helper1.js and Helper2.js as well if I cannot).
Here's my current tsconfig.json:
{
"compileOnSave": true,
"compilerOptions": {
"noImplicitAny": true,
"target": "ES2020",
"module": "ES2020",
"declaration": true
},
"exclude": [
"node_modules"
]
}
There's quite a lot of things you're asking and each of them have several answers so I'll try to provide you with a bit of an overview of your options.
Compiling everything to a single file
One of the things you asked is how you can compile everything to a single file. You can do that in 2 different ways, either using webpack to bundle it for client or using typescript directly.
If you use typescript you have to set outFile to a specific file, in which case it will compile everything to that file, however you can only do that if your module is also set to amd or system, both of which are not ideal. While this works it's something I'd suggest you don't use.
Instead you should use webpack to bundle all your stuff with the output option, in which case webpack will use ts-loader to invoke typescript for you, compile your stuff and bundle it into a single file.
You should also note here that this is only applicable if you actually want to serve it through web and not if you're building a library. If you're building an npm package that you're planning on letting people install with something like npx packageName so that you can use it like import somePackage from some-package, then you should be compiling your stuff to a /lib directory into normal javascript and just let them import it as javascript. There's no reason for why you should provide them with the original typescript in that case.
How to build and distribute it
It really depends on what exactly you're building and how it'll be used, however overall you have 2 main options.
You can either host it somewhere on some server with a domain of your choice so that people can download it. Or you can put it anywhere like a normal git repo where people can download it. In this case you'll have to compile it with webpack yourself, upload it yourself and then just share the link with people i.e. https://example.com/downloads/awesome. Alternatively you can use webpack to render it server side and expose an API to people that they can call in order to get your code, then it will deliver the bundled javascript to them once they call the API i.e. https://api.example.com/awesome which will hit your API with a GET request, which will route to awesome and then you invoke webpack's compiler to bundle your code server side.
Your other option is to build your package like normal, compile it and then use the official npm registry to host your npm package. Using this option will allow people to npx package or npm i package on your code and also allow them to use it like import awesome from 'awesome'. If you go this route then using webpack isn't necessary, or it depends, because people using it will import it into their own project and build it into their own webpack setup and bundle if required. In this case all you have to do is compile your typescript to something like a /lib and allow them to install and import it.
From the things that you're asking/saying it seems to me that you're trying to create an npm package, for that all you need is to create the package, compile your typescript, set up an account on npm and push your package to their registry, from where you can let anyone install it. For this you also shouldn't care at all about compiling all your code to a single file because it doesn't matter, if they use import awesome from 'awesome' then that file can again import anything else inside your own package and they wouldn't know it. You can just tsc your code to an output directory and let them know which is the default export for that package.
If your code has to run in browser then I don't believe just using typescript will be enough, in that case you'll have to use webpack, you might also need babel if you need to support older browsers and polyfills, which is something you can add to webpack, then you'll use webpack to compile your bundle. Webpack will then invoke typescript, through ts-loader, which will compile and bundle your code for you ready for web. In this case you'll still need to push this code to the npm registry as a package so others can use it.
The choice between those options is entirely dependent on what it is and who's going to use it and how.
This should be the core requirements in the tscongfig.json file
{
"compilerOptions": {
"lib": ["es6", "es2020.promise", "dom", "es2020"],
"declaration": true,
"target": "es2020",
"module": "es2020"
}
}
While to pack everything in just one file, I think you have to use something like webpack; but I don't know enough this topic to help you on that, sorry.
I seem to be having difficulty getting my vue js app to run from the dist folder.
I look around this site and found this: Vuejs, Difficulties to build with relative path which gives this solution:
Create a "vue.config.js" file at the main path of your project
Give a relative path. Example:
module.exports = {
publicPath: './'
};
Which I have followed. But when I run
npm run build
and check the file, it is not adding that in.
If it helps here is my version info
"core-js": "^2.6.5",
"vue": "^2.6.10"
Anyone else had this issue or can provide a solution?
You can change the public path that webpack builds by adding this little bit of code to your main.js.
__webpack_public_path__ = 'whatever/path/you/want/to/set/at/runtime'
Webpack create a .p variable in your build files, which has a default value of "/" . so anything you assign to the variable above, will overwrite this and assign whatever prefix you want, in your case "./"
This information comes from https://github.com/vuejs/vue-cli/issues/2944
Maybe I'm trying to do something silly, but I've got a web application (Angular2+), and I'm trying to build it in an extensible/modular way. In particular, I've got various, well, modules for lack of a better term, that I'd like to be able to include or not, depending on what kind of deployment is desired. These modules include various functionality that is implemented via extending base classes.
To simplify things, imagine there is a GenericModuleDefinition class, and there are two modules - ModuleOne.js and ModuleTwo.js. The first defines a ModuleOneDefinitionClass and instantiate an exported instance ModuleOneDefinition, and then registers it with the ModuleRegistry. The second module does an analogous thing.
(To be clear - it registers the ModuleXXXDefinition object with the ModuleRegistry when the ModuleXXX.js file is run (e.g. because of some other .js file imports one of its exports). If it is not run, then clearly nothing gets registered - and this is the problem I'm having, as I describe below.)
The ModuleRegistry has some methods that will iterate over all the Modules and call their individual methods. In this example, there might be a method called ModuleRegistry.initAllModules(), which then calls the initModule() method on each of the registered Modules.
At startup, my application (say, in index.js) calls ModuleRegistry.initAllModules(). Obviously, because index.js imports the exported ModuleRegistry symbol, this will cause the ModuleRegistry.js code to get pulled in, but since none of the exports from either of the two Module .js files is explicitly referenced, these files will not have been pulled in, and so the ModuleOneDefinition and ModuleTwoDefinition objects will not have been instantiated and registered with the ModuleRegistry - so the call to initAllModules() will be for naught.
Obviously, I could just put meaningless references to each of these ModuleDefinition objects in my index.js, which would force them to be pulled in, so that they were registered by the time I call initAllModules(). But this requires changes to the index.js file depending on whether I want to deploy it with ModuleTwo or without. I was hoping to have the mere existence of the ModuleTwo.js be enough to cause the file to get pulled in and the resulting ModuleTwoDefinition to get registered with the ModuleRegistry.
Is there a standard way to handle this kind of situation? Am I stuck having to edit some global file (either index.js or some other file it references) so that it has information about all the included Modules so that it can then go and load them? Or is there a clever way to cause JavaScript to execute all the .js files in a directory so that merely copying the files it would be enough to get them to load at startup?
a clever way to cause xxJavaScriptxx Node.js to execute all the .js files in a directory:
var fs = require('fs') // node filesystem
var path = require('path') // node path
function hasJsExtension(item) {
return item != 'index.js' && path.extname(item) === '.js'
}
function pathHere(item) {
return path.join('.', item)
}
fs.readdir('./', function(err, list) {
if (err) return err
list.filter(hasJsExtension).map(pathHere).forEach(require) // require them all
})
Angular is pretty different, all the more if it is ng serve who checks if your app needs a module, and if so serves the corresponding js file, at any time needed, not at first load time.
In fact your situation reminds me of C++ with header files Declaration and cpp files with implementation, maybe you just need a defineAllModules function before initAllModules.
Another way could be considering finding out how to exclude those modules from ng-serve, and include them as scripts in your HTML before the others, they would so be defined (if present and so, served), and called by angular if necesary, the only cavehat is the error in the console if one script tag is not fetched, but your app will work anyway, if it supposed to do so.
But anyway, it would be declaring/defining those modules somewhere in ng-serve and also in the HTML.
In your own special case, and not willing to under-evalute ng-serve, but is the total js for your app too heavy to be served at once? (minified and all the ...), since the good-to-go solution may be one of the many tools to build and rebuild your production all.js from your dev js folder at will, or like you said, with a drag&drop in your folder.
Such tool is, again, server-side, but even if you only can push/FTP your javascript, you could use it in your prefered dev environment and just push your new version. To see a list of such tools google 'YourDevEnvironment bundle javascript'.
To do more with angular serve and append static js files under specific conditions, you should use webpack so the first option i see here is eject your webpack configuration and after that you can specify what angular should load or not.
With that said, i will give an example:
With angular cli and ng serve any external javascript files you wanna include, you have to put them inside the scripts array in the angular-cli.json file.However you can not control which file should be included and which one not.
By using webpack configuration you can specify all these thing by passing a flag from your terminal to the webpack config file and do all the process right there.
Example:
var env.commandLineParamater, plugins;
if(env.commandLineParamater == 'production'){
plugins = [
new ScriptsWebpackPlugin({
"name": "scripts",
"sourceMap": true,
"filename": "scripts.bundle.js",
"scripts": [
"D:\\Tutorial\\Angular\\demo-project\\node_moduels\\bootstrap\\dist\\bootstrap.min.js",
"D:\\Tutorial\\Angular\\demo-project\\node_moduels\\jquery\\dist\\jquery.min.js"
],
"basePath": "D:\\Tutorial\\Angular\\demo-project"
}),
]}else{
plugins = [
new ScriptsWebpackPlugin({
"name": "scripts",
"sourceMap": true,
"filename": "scripts.bundle.js",
"scripts": [
"D:\\Tutorial\\Angular\\demo-project\\node_moduels\\bootstrap\\dist\\bootstrap.min.js"
],
"basePath": "D:\\Tutorial\\Angular\\demo-project"
}),
]
}
then:
module.exports = (env) => {
"plugins": plugins,
// other webpack configuration
}
The script.js bundle will be loaded before your main app bundle and so you can control what you load when you run npm run start instead of ng-serve.
To Eject your webpack configuration, use ng eject.
Generally speaking, when you need to control some of angular ng-serve working, you should extract your own webpack config and customize it as you want.
I'm trying to build a standalone JS lib that uses backbone/jquery. Here's a sample repo that represents the lib I'm trying to build.
I have a few goals for this lib:
Don't expose ANY globals (ie wrap everything properly so nothing leaks)
Don't be affected by any other package tools like requireJS
This lib could be loaded into any other website and I don't want any conflicts with JS that might be there (which is beyond my control) such as requireJS
So, my understanding is that I can use browserify-shim to properly shim these modules such that they can be used as expected with require in browserify. I'm pretty sure the shim is supposed to undef defines and require when it wraps these modules, which would achieve goal #2. Goal #1 would hopefully then be achieved just by using browserify as expected.
I can't really seem to get this to work and I"m not sure if it's just my misunderstanding of browserify-shim. I seem to have the same problem whether or not I run browserify from the command line: browserify src/main.js -o build/main.js or using the gulp build.
Basically, it's as if browserify-shim isn't even running. If you open up the index.html, you can see that the requirejs code I've loaded in is definitely affecting my lib as Backbone is just an empty object, then requirejs crashes.
You can see my package.json here in the repo, and it looks roughly like this:
{
...
"browserify-shim": {
"./node_modules/jquery/dist/jquery.js": "$",
"./node_modules/underscore.js": "_",
"./node_modules/backbone.js": {
"exports": "Backbone",
"depends": [
"./node_modules/underscore.js",
"./node_modules/jquery/dist/jquery.js"
]
}
},
"devDependencies": {
"browserify": "^4.1.11",
"browserify-shim": "^3.6.0",
"gulp": "^3.8.1",
"vinyl-source-stream": "^0.1.1"
},
"dependencies": {
"backbone": "^1.1.2",
"jquery": "^1.11.0",
"underscore": "^1.6.0"
}
}
Not sure what I'm missing here, but I'm having no luck. I was wondering if anyone can help or at least corroborate that what I'm trying to achieve is correct and possible with browserify-shim.
Thanks.
edit
To illustrate that it's definitely not shimming the modules, I've created a build/main.js and a build/main-shimmed.js that are exactly the same. The main.js I ran with no shim config and the shimmed one had the above config. I'm clearly missing something here.
Browserify and Browserify-shim are working in this case, and your main.js is calling the right require.
The problem you are running into is the that libraries you are bundling are also attempting to wire up the AMD style configuration. Your "crossing the streams", beware of Gozer.
Either get versions without the AMD configuration loaders, or comment them out.
I'm trying to minify and concatenate require modules together into one file.
For example, if I have a file called article.js like the following:
define(["jquery","flexslider","share_div"],function(){});
I want all of those dependencies to be combined and minified into one file.
I have the following in my build script, but it's not combining files together, just minifying.
This is my build.js file:
{
"appDir": "../js",
"baseUrl": "../js",
"dir": "../www-build",
"mainConfigFile": "../js/common.js",
"modules": [
{
"name": "common"
},
{
"name": "page/article",
"exclude": ["common"]
}
],
"paths": {
"jquery": "empty:",
"jquery_ui": "empty:",
"twitter_bootstrap": "empty:"
}
}
My understanding of Require and the build script may be wrong, but I envisioned that files would be concatenated together.
Any guidance would be appreciated.
To also minify the nested dependencies of the individual modules that are traced as dependencies, specify this additonal property as part of your build file: findNestedDependencies: true. (See line 318-324 of the full RequireJS doc.)
This is if articles.js is a dependency found in common.js and you are tracing through common.js to build a concatenated and minfied file. I am not sure about the lack of concatenation if you are just trying to build the individual module itself.
The build.txt file you've included in a comment (the comment I'm referring to has been deleted by Andy after I wrote my answer) is a bit hard to read since it has not been included as preformatted code. From what I can see in the file you've provided, r.js does indeed concatenate the dependencies of article.js into one file. So it appears that you've solved your original question somehow.
As so why your output is not appearing in the right directory, I can only speculate. The build.js file you've shown in your question is set to put everything in ../www-build, however your build.txt file shows the output being stored in ../js. Here's the speculation part: sometimes when we are in the midst of trying to find solutions, we make changes here and there and lost track of where we were. That certainly happened to me. What I do then is clean my development tree (remove the builds), inspect my build configuration to make sure that what I think is used is indeed used and rerun the build.