webpack multiple output path, limit public access, or custom index.html? - node.js

I am using node-express, with typescript.
my folder is setup as follows:
.dist
public
public.js
index.html
server.js
node_modules
src
classes
namespace1
module1
public
app - all angular files.
main.ts
routes
index.ts
app.ts
package.json
tsconfig.json
webpack.config.js
Now, I need webpack to output 2 files to /public/public.js and /server.js at .dist folder. nodejs will then run from .dist/server.js, and I want to separate public.js to prevent client to access server.js
I also use html-webpack-plugin to generate html files.
I have tried using a little hack like
entry: {
"server": "./src/app.ts",
"public/public": "./src/public/main.ts"
}
but then html-webpack-plugin made index.html to load script from /public/public.js instead of public.js
Now, I think we can solve this in 3 way.
Let server.js send public.js using http://localhost/public.js, but it will make managing static folder a little bit complicated. but I will think some way to trick it. Question: how to serve public.js via server.js?
Set entry to "public": "./src/public/main.ts". Question: how to put that public.js into public folder?
Setup html-webpack-plugin to load from /public.js instead of /public/public.js and make index.html inside /public folder. As of now, html-webpack-plugin generates <script type="text/javascript" src="../public/polyfill.js"></script><script type="text/javascript" src="../public/public.js"></script></body> where is should make <script type="text/javascript" src="/polyfill.js"></script><script type="text/javascript" src="/public.js"></script></body>
Question: How to do that?
Or is there any other idea to solve this? I am open to any suggestion.
Thank you

I think I can answer scenarios 2 and 3.
2- Apart of setting up entry points, you can set up some output configuration. http://webpack.github.io/docs/configuration.html#output
3- Also you could use copy webpack plugin to copy the files you need into your public folder.
https://github.com/kevlened/copy-webpack-plugin
I do it in one of my projects, this is the code that I add on the webpack config file:
new CopyWebpackPlugin([
{from: __dirname + '/src/public'}
])
Hope this helps.
Regards.

I managed by using this config.
module.exports = [
{
entry: "./src/app.ts",
output: {
filename: "server.js",
path: __dirname + "/dist"
},
target: "node",
resolve: {
extensions: ['.ts', '.js', '.tsx', '.jsx']
},
node: {
__dirname: false
},
module: { // all modules here for server
}
}, {
entry: "./src/public/main.ts",
output: {
filename: "bundle.js",
path: __dirname + "/dist/public"
},
target: "web",
plugins: [
new htmlPlugin({
filename: 'index.html'
})
],
resolve: {
extensions: ['.ts', '.js', '.tsx', '.jsx']
},
module: { // all your modules here.
}
}
]

Related

compile typescript to js with webpack and keep directory structure

I want webpack to compile my typescript node project into js but I want it to maintain the directory structure and not bundle into 1 file.
Is this possible?
My structure is:
src
|_controllers
|_home
|_index.ts
|_ services
// etc.
And I want it to compile to:
dist
|_controllers
|_home
|_index.ts
|_ services
// etc.
currently my config is like this:
{
name: 'api',
target: 'node',
externals: getExternals(),
entry: isDevelopment ? [...entries] : entries,
devtool: !isDevelopment && 'cheap-module-source-map',
output: {
path: paths.appBuild,
filename: '[name].js',
libraryTarget: 'commonjs2'
},
plugins: [
new WriteFilePlugin(),
new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1
}),
isProduction && new webpack.optimize.ModuleConcatenationPlugin()
]
}
Is it possible with webpack?
I can't use just tsc because I have a yarn workspaces monorepo and I might have a link reference like this:
import {something} from '#my/package';
#my/package does not exist in npm and only exists in the context of the monorepo, I can use node externals with webpack to include it in the bundle I don't think I can keep the folder structure this way.
Would the new typescript 3.0 project references solve this problem?
Understood your concerns. Looks it's completely doable by transpile-webpack-plugin. You may setup it as below:
const TranspilePlugin = require('transpile-webpack-plugin');
module.exports = {
// ...
entry: './src/controllers/home/index.ts',
output: {
path: __dirname + '/dist',
},
plugins: [new TranspilePlugin({ longestCommonDir: './src' })],
};
The plugin works well with webpack resolve.alias and externals. All the files directly or indirectly imported by the entry will be collected and compiled into the output dir seperately without bundling but with keeping the directory structure and file names. Just have a try. 🙂

My tsconfig.json cannot find a module in my node_modules directory, not sure what is wrong

I have the following hierarchy:
dist/
|- BuildTasks/
|- CustomTask/
- CustomTask.js
node_modules/
source/
|- BuildTasks/
|- CustomTask/
- CustomTask.ts
- tsconfig.json
Additionally, I am trying to create a VSTS Task extension for internal (private) usage. Originally, I had my tsconfig.json at my root directory, and everything worked just fine on my local machine. The problem is that a VSTS Extension requires all the files to be included in the same directory as the task folder itself. See https://github.com/Microsoft/vsts-task-lib/issues/274 for more information:
you need to publish a self contained task folder. the agent doesnt run
npm install to restore your dependencies.
Originally, I had a this problem solved by include a step to copy the entire node_modules directory into each Task folder, in this case my CustomTask folder which contains my JS file. But, this seems a bit much considering that not every task I am writing has the same module requirements.
My idea was to create a tsconfig.json in each of the Task folders which would specify to create a single output file containing all of the dependent modules, but unfortunately it is not working:
{
"compilerOptions": {
"baseUrl": ".",
"target": "ES6",
"module": "system",
"strict": true,
"rootDir": ".",
"outFile": "../../../dist/BuildTasks/CustomTask/CustomTask.js",
"paths": {
"*" : ["../../../node_modules/*"]
}
}
}
Prior to adding the "paths", I was getting the following errors:
error TS2307: Cannot find module 'vsts-task-lib/task'.
error TS2307: Cannot find module 'moment'.
After adding the paths, I still get the error that it cannot find the module 'moment', which is in my node_modules directory. Also, when I look at the output JS it seems that it didn't include the 'vsts-tasks-lib' code necessary, maybe because it still had an error in regards to the 'moment' module? Not sure what I missed?
Using webpack to compile JavaScript modules, simple sample:
webpack.config.js:
const path = require('path');
module.exports = {
entry: './testtask.ts',
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: ['.tsx', '.ts', '.js']
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
node: {
fs: 'empty'
},
target: 'node'
};
After that, there are just bundle.js and task.json in task folder.
Update: sample code in testtask.ts:
import tl = require('vsts-task-lib/task');
import fs = require('fs');
console.log('Set variable================');
tl.setVariable('varCode1', 'code1');
tl.setTaskVariable('varTaskCode1', 'taskCode1');
var taskVariables = tl.getVariables();
console.log("variables are:");
for (var taskVariable of taskVariables) {
console.log(taskVariable.name);
console.log(taskVariable.value);
}
console.log('##vso[task.setvariable variable=LogCode1;]LogCode1');
console.log('end========================');
console.log('current path is:' + __dirname);
fs.appendFile('TextFile1.txt', 'data to append', function (err) {
if (err) throw err;
console.log('Saved!');
});
console.log('configure file path:' + process.env.myconfig);
console.log('configure file path2:' + process.env.myconfig2);

"You may need an appropriate loader for this file type", webpack can't parse angular2 file

I'm trying to get a very simple Angular2 app working, with Webpack as a module bundler. I'm following this code, and I copied all the configuration files as they are, only changing file paths. However, when I run npm-start, I get the following error, which I think is a Webpack error:
ERROR in ./hello.js
Module parse failed: /home/marieficid/Documentos/cloud/cloud/hello.js Line 1: Unexpected token
You may need an appropriate loader to handle this file type.
| import {bootstrap} from "angular2/platform/browser";
| import {Component} from "angular2/core";
|
# ./app.ts 2:0-21
As a result, the Angular2 code in my app isn't loaded.
This is my app.ts:
import "./hello.js";
This is hello.js, where the error seems to be (which I take to mean that webpack parsed app.ts just fine):
import {bootstrap} from "angular2/platform/browser";
import {Component} from "angular2/core";
#Component({
selector: 'app',
template: '<div>Hello world</div>'
})
class App{}
bootstrap(App);
And this iswebpack.config.js:
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var path = require('path');
module.exports = {
entry: {
'app': './app.ts',
'vendor': './vendor.ts'
},
output: {
path: "./dist",
filename: "bundle.js"
},
plugins: [
new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.bundle.js'),
new HtmlWebpackPlugin({
inject: false,
template: './index.html'
})
],
resolve: {
extensions: ['', '.ts', '.js']
},
module: {
loaders: [
{ test: /\.ts$/, loader: 'ts-loader' },
],
noParse: [ path.join(__dirname, 'node_modules', 'angular2', 'bundles') ]
},
devServer: {
historyApiFallback: true
}
};
All these files and node_modules are in the same directory.
I have found similar questions online but nothing worked for me. I also didn't install babel because the sample code I'm using as base doesn't use it, but if it's necessary I'm will.
As suggested by #napstablook
Since in your webpack.config.js file you have
resolve: {
extensions: ['', '.ts', '.js']
},
Webpack will try to handle those .js files but it needs a specific loader to do so which is, if I'm not wrong, script-loader.
In your case the solution is as simple as deleting the .js files, or changing their extension to be .ts.
For me this issue occurred when I ran ng test,
please check below points,
Console will list out the files that is causing the error.
Check the html file is correctly mapped from the typescript.
styleUrls file should point to the CSS file not html, this is the mistake I
did.
this error also comes up for me in angular forms when i had patch value set then an extra = sign
ncont.controls[position].patchValue({[cardname]:file}) = file
which is a dumb part on me and angular for not telling me

RequireJS baseUrl and multiple optimized files

I've separated out my 3rd party libraries from my app code and grouped them all together into a vendor.js file for requirejs to pull in. In my build.js file, I'm using the modules syntax to optimize my main application, excluding the vendor scripts, and to optimize the vendor.js file. The only issue I'm having is when my compiled main module requests vendor, it's getting the baseUrl from the config file and so doesn't load the optimized vendor.js file. My build.js file looks like this:
({
baseUrl: "js",
dir: "build",
mainConfigFile: "js/main.js",
removeCombined: true,
findNestedDependencies: true,
skipDirOptimize: true,
inlineText: true,
useStrict: true,
wrap: true,
keepBuildDir: false,
optimize: "uglify2",
modules: [
{
name: "vendor"
},
{
name: "main",
exclude: ["vendor"]
}
]
})
And my main.js file looks like this:
requirejs.config({
baseUrl: "js",
paths: {
jquery: 'vendor/jquery/jquery-2.1.3.min',
bootstrap: 'vendor/bootstrap/bootstrap.min',
handlebars: 'vendor/handlebars/handlebars-v2.0.0',
backbone: 'vendor/backbone/backbone-min',
underscore: 'vendor/lodash/lodash.underscore',
marionette: 'vendor/marionette/backbone.marionette.min',
models: 'common/models',
collections: 'common/collections'
}
});
define(['module', 'vendor'], function(module) {
var configPath = "config/config." + module.config().env;
require([configPath, 'app', 'jquery'], function(config, Application, $) {
$(function() {
// Kick off the app
Application.start(config);
});
});
});
All development is done in the js folder, and my build.js file is outside that folder. The optimized files end up in build, a sibling to js, but when I include my main file like this:
<script data-main="build/main" src="js/vendor/require/require.max.js"></script>
It ends up loading js/vendor.js for that define() call. What am I missing here? How can I tell the optimized main file to load build/vendor.js instead, yet allow the unoptimized version to still load js/vendor.js?
Ok, I figured this out. It was simple, really, just a case of too much configuration. When you load your script using data-main, the baseUrl is set relative to that file. So, if I specified js/main, the baseUrl would be js. But, since I explicitly specified baseUrl in the config block of main.js, that gets overridden, both in development and production. By removing baseUrl: "js" from main.js, everything works as expected. The development build loads everything relative to js and the production build loads everything (vendor.js) relative to build when I change data-main to build/main. Hope this helps somebody else someday.
requirejs.config({
paths: {
jquery: 'vendor/jquery/jquery-2.1.3.min',
...
}
});
// 'vendor' is loaded relative to whatever directory main.js is in
define(['module', 'vendor'], function(module) {
...
});

require cdn libraries in browserify without bundling it in the final js file

If I have a library that is being pulled down from cdn and wouldn't like it to be part of the final js file but be able to require it using browserify, how would I solve it?
Here is how I currently solve it using alias and a shim file.
browserify: {
options: {
debug: true,
transform: [ 'reactify' ],
alias: [
'client/shims/jquery.js:jquery'
]
},
app: {
src: 'client/app.js',
dest: 'public/app.js'
}
}
here is the shim file client/shims/jquery.js which I alias to jquery so I can use require('jquery') instead of the full path.
module.exports = $;
Is there a shortcut in grunt-browserify to support this scenario? I would like to know if it is possible to define it in Gruntfile.js without creating the shim file.
Adding external: [ 'jquery' ] seems to totally ignore it and doesn't work.
With browserify-shim you can add this in your package.json file:
"browserify": {
"transform": [
"browserify-shim"
]
},
"browserify-shim": {
"jquery": "global:$"
}
Then jquery will be available in your modules via require('jquery')
If you load jQuery before the Browserify bundle on the page, $ will be available as a global.

Resources