How to process compiled Vue's component in webpack? - javascript

I want to create a "macro" similar to gettext in Django for Vue.js.
How can I setup a loader so that it will get a compiled (parsed) Vue.js component's template tag? It can be either JS or some AST.
Example:
// webpack.config.js
module.exports = {
module: {
rules: [{
test: /.vue$/,
loader: 'vue-loader'
}, {
test: /.vue?template/ // just an example
loader: myLoader
}]
}
}
// my-loader.js
module.exports = function (source) {
// here source is either JS or AST representation
return source
}

Simple config can look like this:
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.vue$/,
use: [
'vue-loader',
'my-loader', // our custom loader
]
}
]
}
}
// my-loader.js
const { createDefaultCompiler } = require('#vue/component-compiler')
module.exports = function(source) {
const compiler = createDefaultCompiler()
const descriptor = compiler.compileToDescriptor('filename.vue', source)
const { template } = descriptor;
console.log(template.ast) // AST of the component's template tag
}

Related

Cannot call functions after running browserify

I have a an es6 JS class below which I am running through browserify to output in es5. Below is my es6 JS class:
import $j from "jquery";
import BaseComponent from './Components/base-component';
class QuestionnaireView extends BaseComponent {
constructor() {
super();
this.defaultOptions = {
questionId : '#questionId',
responseId : '#responseId',
answerId : '#answerId',
questionTextId : '#questionTextId'
};
this.state = {
};
}
initChildren() {
}
addListeners() {
}
collectQuestions() {
var questionAndAnswersDict = [];
var answersAndWeightingsDict = [];
$j(this.options.questionId).each(function () {
var questionText = $j(this).find("input")[0].value;
$j(this.options.answerId).each(function () {
var answerText = $j(this).find("input")[0].value;
var weighting = $j(this).find("input")[1].value;
answersAndWeightingsDict.push({
key: answerText,
value: weighting
});
});
questionAndAnswersDict.push({
key: questionText,
value: answersAndWeightingsDict
});
});
}
collectResponses() {
var responsesDict = [];
var weightingDict = [];
$j(this.options.responseId).each(function () {
var minWeighting = $j(this).find("input")[0].value;
var maxWeighting = $j(this).find("input")[1].value;
var responseText = $j(this).find("input")[2].value;
weightingDict.push({
key: minWeighting,
value: maxWeighting
});
responsesDict.push({
key: responseText,
value: weightingDict
});
});
}
}
export default () => { return new QuestionnaireView(); };
And here is the browserify command I am running:
browserify Scripts/questionnaire-view.js -o wwwroot/js/questionnaire-view.js
I have also tried
browserify Scripts/questionnaire-view.js -o wwwroot/js/questionnaire-view.js -t [ babelify --presets [ #babel/preset-env #babel/preset-react ] --plugins [ #babel/plugin-transform-modules-commonjs ] ]
The output JS file looks okay and does not throw any errors in dev tools but when I go to call a function I get the following:
Error: Microsoft.JSInterop.JSException: Could not find 'collectQuestions' ('collectQuestions' was undefined).
Error: Could not find 'collectQuestions' ('collectQuestions' was undefined).
at http://localhost:41131/_framework/blazor.server.js:1:288
at Array.forEach (<anonymous>)
at r.findFunction (http://localhost:41131/_framework/blazor.server.js:1:256)
at v (http://localhost:41131/_framework/blazor.server.js:1:1882)
at http://localhost:41131/_framework/blazor.server.js:1:2662
at new Promise (<anonymous>)
at et.beginInvokeJSFromDotNet (http://localhost:41131/_framework/blazor.server.js:1:2643)
at http://localhost:41131/_framework/blazor.server.js:1:62750
at Array.forEach (<anonymous>)
at et._invokeClientMethod (http://localhost:41131/_framework/blazor.server.js:1:62736)
Any help is greatly appreciated :)
I ended up using webpack with babel loader:
var devJSConfig = Object.assign({}, config, {
mode: 'development',
entry: [
path.resolve(__dirname, './Scripts/Components/base-component.js'),
path.resolve(__dirname, './Scripts/address-view.js'),
path.resolve(__dirname, './Scripts/customer-view.js'),
path.resolve(__dirname, './Scripts/questionnaire-view.js')
],
output: {
path: path.resolve(__dirname, 'wwwroot/js'),
filename: "[name].js"
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: '/node_modules/',
use: [
{
loader: 'babel-loader',
options: {
presets: [
"#babel/preset-env"
]
}
}
]
}
]
}
});
In my _Host.cshtml I had the script tag type attribute for my js files set to 'text/javascript' when it needs to be 'module'. I was also linking the individual script files but only needed to reference the bundle js which was produced using the above.
Lastly in my script I had to expose the js class to the Window like so (place this at the end of your js class):
window['QuestionnaireView'] = new QuestionnaireView();
I could then call js functions in my Blazor component class using:
var test = await jSRuntime.InvokeAsync<Object>("QuestionnaireView.collectQuestions");

Importing Bootstrap JavaScript, Webpack

I'm trying to import only collapse component from Bootstrap JS components like this:
import "../../../node_modules/bootstrap/js/dist/collapse";
Everything is fine except jQuery, this file (collapse.js) is requiring jquery and it's get compiled to my main.js file, how to avoid that? I have included jQuery from CDN.
Here is my Gulp/Webpack config
let webpackConfig = {
module: {
rules: [
{
test: /.js$/,
use: [
{
loader: 'babel-loader'
}
]
}
]
}
};
// Combine JavaScript into one file
// In production, the file is minified
function javascript() {
return gulp.src(PATHS.entries)
.pipe(named())
.pipe($.sourcemaps.init())
.pipe(webpackStream(webpackConfig, webpack2))
.pipe($.if(PRODUCTION, $.uglify()
.on('error', e => { console.log(e); })
))
.pipe($.if(!PRODUCTION, $.sourcemaps.write()))
.pipe(gulp.dest(PATHS.dist + '/assets/js'));
}
UPDATE, SOLUTION:
let webpackConfig = {
externals: { jquery: 'jQuery' },
module: {
rules: [
{
test: /.js$/,
use: [
{
loader: 'babel-loader'
}
]
}
]
}
};
You can use the externals settings from Webpack to stop it from including some packages into your bundle.

Webpack: Trying to expose a bundled object to be usable by other scripts, object is still undefined

I'm trying to get just the basics down, transpiling a jsx file to js. However, my transpiled code needs to be called by non-transpiled code. output.library is supposed to help with that.
In the resulting bundle I see a definition for var react. But just after stepping through the entire bundle, it's clear react still isn't getting set.
my webpack.config.js
var webpack = require('webpack');
var path = require('path');
module.exports = {
entry: "./public/js/ui/react/dialog.jsx",
output: {
path: path.resolve(__dirname, "public/js/ui/react/"),
filename: "bundle.js",
libraryTarget: "var",
library: "react"
},
resolve: {
extensions: ['.js', '.jsx']
},
module: {
rules: [
{
test: /\.jsx$/,
loader: 'babel-loader',
exclude: [
path.resolve(__dirname, "node_modules/")
],
query: {
presets: ['es2015', "react"]
}
}
]
},
node: {
fs: "empty"
}
}
and the jsx I am trying to transpile:
'use strict';
react.Dialog = class extends React.Component {
render() {
return (
<div class="bubble-speech">Hello World</div>
)
}
}
elsewhere in my code, AND BEFORE THE BUNDLE, I have this, so that the react.Dialog assignment is not a null reference error:
var react = {};
If I take that one line away, the bundle.js will throw an error trying to assign react.Dialog. But if I leave it in, var react remains set to the empty object. That seems like a contradiction! What am I missing here?
I think react should be set as an externally defined global var, like this:
{
output: {
// export itself to a global var
libraryTarget: "var",
// name of the global var: "Foo"
library: "Foo"
},
externals: {
// require("react") is external and available
// on the global var React
"react": "React"
}
}

Babel loader failing

I have the following example class, containing an example class property arrow function:
class ExampleClass {
example = (params) => {
return params
}
}
Unfortunately this construction isn't yet being well recognized:
ERROR in ./node_modules/example/example.js
Module parse failed: Unexpected token (284:12)
You may need an appropriate loader to handle this file type.
|
| example = (params) => {
| return params
| }
# ./src/example/index.js 8:17-48
# ./src/index.js
# multi (webpack)-dev-server/client?http://localhost:8081 ./src
I have been using the following babel presets and webpack configurations:
Webpack Config
const HtmlWebPackPlugin = require("html-webpack-plugin");
const htmlPlugin = new HtmlWebPackPlugin({
template: "./src/index.html",
filename: "./index.html"
});
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
}
]
},
plugins: [htmlPlugin]
};
.babelrc
{
"presets": ["env", "react", "es2015", "stage-2"]
}
I have no idea what else I need to import here, I'll admit I'm not exactly sure how I would find that out through googling.
I think you can declare method in class with this syntax.
class ExampleClass {
example(params) {
return params
}
}

Webpack loaders in multiple config files

I have a Webpack flow where multiple configurations are merged depending on the type of build. I'm still a webpack newbie but getting the hang of it - but ran into a problem.
With the setup I have som css loaders which are used in my common flow - that is on every build. Now I need some loaders only used for production builds. With my current setup the loaders for production is never used - but if I outcomment the loaders in my common setup the production loaders are run.
Is there a way to merge rules for css from different configurations?
My webpack.config.js
const path = require('path');
const webpackMerge = require('webpack-merge');
const commonPartial = require('./webpack/webpack.common');
const clientPartial = require('./webpack/webpack.client');
const serverPartial = require('./webpack/webpack.server');
const prodPartial = require('./webpack/webpack.prod');
const { getAotPlugin } = require('./webpack/webpack.aot');
module.exports = function (options, webpackOptions) {
options = options || {};
webpackOptions = webpackOptions || {};
if (options.aot) {
console.log(`Running build for ${options.client ? 'client' : 'server'} with AoT Compilation`)
}
let serverConfig = webpackMerge({}, commonPartial, serverPartial, {
entry: options.aot ? { 'main-server' : './Client/main.server.aot.ts' } : serverPartial.entry, // Temporary
plugins: [
getAotPlugin('server', !!options.aot)
]
});
let clientConfig = webpackMerge({}, commonPartial, clientPartial, {
plugins: [
getAotPlugin('client', !!options.aot)
]
});
if (options.prod) {
// Change api calls prior to packaging due to the /web root on production
clientConfig = webpackMerge({}, prodPartial, clientConfig);
serverConfig = webpackMerge({}, prodPartial, serverConfig);
}
const configs = [];
if (!options.aot) {
configs.push(clientConfig, serverConfig);
} else if (options.client) {
configs.push(clientConfig);
} else if (options.server) {
configs.push(serverConfig);
}
return configs;
}
My webpack.common.js
const { root } = require('./helpers');
const path = require('path');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
/**
* This is a common webpack config which is the base for all builds
*/
const extractBeneath = new ExtractTextPlugin('../assets/stylesheets/beneath.css');
const extractSkolePlan = new ExtractTextPlugin('../assets/stylesheets/skoleplan.css');
const source = path.resolve(__dirname, 'Client');
const appDirectory = path.resolve(source, 'app');
module.exports = {
devtool: 'source-map',
resolve: {
extensions: ['.ts', '.js']
},
output: {
filename: '[name].js',
publicPath: '/dist/' // Webpack dev middleware, if enabled, handles requests for this URL prefix
},
module: {
rules: [
{ test: /\.ts$/, loader: '#ngtools/webpack' },
{
//***** This is working nicely *****
test: /\.css$/,
exclude: appDirectory,
use: extractSkolePlan.extract({
fallback: 'to-string-loader',
use: 'css-loader?sourcemap'
})
},
{
//***** This is working nicely too *****
test: /\.css$/,
include: appDirectory,
use: 'raw-loader'
},
{ test: /\.html$/, loader: 'html-loader' },
{
test: /\.less$/,
use: extractBeneath.extract({
fallback: 'to-string-loader',
use: ['css-loader', 'less-loader']
})
},
{ test: /\.(woff2?|ttf|eot|svg)$/, loader: 'url-loader?limit=10000' },
{ test: /\.(png|jpg|jpeg|gif)$/, loader: 'url-loader?limit=25000' }
]
}
,
plugins: [
extractSkolePlan,
extractBeneath
]
};
And my webpack.prod.js
const { root } = require('./helpers');
const path = require('path');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const StringReplacePlugin = require("string-replace-webpack-plugin");
const source = path.resolve(__dirname, 'Client');
const appDirectory = path.resolve(source, 'app');
/**
* This is a prod config to be merged with the Client config
*/
module.exports = {
module: {
rules: [
{
test: /\.ts$/,
loader: StringReplacePlugin.replace({
replacements: [
{
pattern: /return window.location.origin;/ig,
replacement: function() {
console.log(' Her er javascript replaced');
return 'return window.location.origin + \'/web\'';
}
}
]
})
},
{
//***** This is never loaded *****
test: /\.css$/,
exclude: appDirectory,
use: StringReplacePlugin.replace({
replacements: [
{
pattern: /assets/ig,
replacement: function() {
console.log('Her er css skiftet');
return '/web/assets/martin';
}
}
]
})
}
]
},
plugins: [
// an instance of the plugin must be present
new StringReplacePlugin()
]
};
Any help is appreciate - thanks :-)
I am not familiar with the package you are using, webpack-merge, but when testing, it is similar to:
Object.assign({}, {foo: 'a'}, {foo: 'b'}) // => {foo: 'b'}
It gives priority to the objects from right to left. So in your example, it should be:
if (options.prod) {
// Change api calls prior to packaging due to the /web root on production
clientConfig = webpackMerge({}, clientConfig, prodPartial);
serverConfig = webpackMerge({}, serverConfig, prodPartial);
}
With prodPartial at the right, in order to get preference over the commonPartial inside serverConfig and clientConfig.
However, I would advise to make these three configuration files easier to reason about by having several imports, e.g. module.exports.output, module.exports.rules, and do the merging manually in the main config file.

Categories

Resources