Using webpack I am trying to export a function from domManipulation.js to index.js, they are both in src folder
export function displayPage(pageToDisplay) {
pageToDisplay.classlist.remove("hide");
pagesArray.forEach((page) => {
if (page !== pageToDisplay) {
page.classList.add("hide");
}
});
}
using
import { displayPage } from "./domManipulation.js";
but in index.js vs code just greys out as if the import text isn't working. If I right click on displayPage and select go to definition vs code takes my to the function so I guess it knows where the import statement is pointing to? However if I try and run the function in the chrome console on index.html from the dist folder I get displayPage(ADD_EDIT_PAGE); VM18270:1 Uncaught ReferenceError: displayPage is not defined at <anonymous>:1:1
Please help and be kind I literally started using webpack like 2 days ago!
below is the the webpack config, the full code from the file I am trying to export the function from and the code from the module as it appears in chrome.
const path = require("path");
module.exports = {
// watch: true,
mode: 'development',
entry: "./src/index.js",
output: {
filename: "main.js",
path: path.resolve(__dirname, "dist"),
},
module: {
rules: [
{
test: /\.css$/i,
use: ["style-loader", "css-loader"],
},
],
},
};
const DELETE_TODO_PAGE = document.getElementById("deleteTodoPage");
const CLEAR_COMPLEATED_PAGE = document.getElementById("clearCompleatedPage");
const NOTES_PAGE = document.getElementById("notesPage");
const TODO_PAGE = document.getElementById("todoPage");
const ADD_EDIT_PAGE = document.getElementById("addEditPage");
let pagesArray = [
DELETE_TODO_PAGE,
CLEAR_COMPLEATED_PAGE,
NOTES_PAGE,
TODO_PAGE,
ADD_EDIT_PAGE,
];
export function displayPage(pageToDisplay) {
pageToDisplay.classlist.remove("hide");
pagesArray.forEach((page) => {
if (page !== pageToDisplay) {
page.classList.add("hide");
}
});
}
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "displayPage", function() { return displayPage; });
const DELETE_TODO_PAGE = document.getElementById("deleteTodoPage");
const CLEAR_COMPLEATED_PAGE = document.getElementById("clearCompleatedPage");
const NOTES_PAGE = document.getElementById("notesPage");
const TODO_PAGE = document.getElementById("todoPage");
const ADD_EDIT_PAGE = document.getElementById("addEditPage");
function displayPage(pageToDisplay) {
let pagesArray = [
DELETE_TODO_PAGE,
CLEAR_COMPLEATED_PAGE,
NOTES_PAGE,
TODO_PAGE,
ADD_EDIT_PAGE,
];
pageToDisplay.classlist.remove("hide");
pagesArray.forEach((page) => {
if (page !== pageToDisplay) {
page.classList.add("hide");
}
});
}
Related
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");
I'm currently iterating over a few different directories, requiring Webpack Configuration objects from these different directories, and starting a child process in node that runs the webpack compiler with that given configuration object.
When I use require( webpackConfigPath ), it strips out the regex values for module: { rules : [ { test: /\.js$/, ...} ]}, and replaces them with an empty object, bringing over something that looks like this: module: { rules : [ { test: {}, ...} ]}
Can anybody provide me direction on how to deep clone a variable from another file without these RegExp key values being stripped out and replaced with empty objects?
Example in usage:
const webpackConfigDir = path.resolve( __dirname, themeDir + '/webpack.config.js' )
let config = require( webpackConfigDir );
I ended up solving this by requiring a constructor function for the webpack configuration instead.
Example:
webpack.config.js
const path = require("path");
const entryPath = path.resolve( __dirname, 'source/scripts/app.js' );
const outputPath = path.resolve( __dirname, 'dist/js' )
const constructConfig = function ( entryPath, outputPath ) {
const config = {
entry: ["#babel/polyfill", entryPath],
output: {
path: outputPath,
filename: "scripts.js"
},
module: {
rules: [
{
test: /\.js$/,
use: "babel-loader",
exclude: /node_modules/
}
]
},
plugins: [],
devtool: 'source-map',
externals: {
jquery: 'jQuery'
}
}
return config
}
module.exports = {
constructor: constructConfig,
entry: entryPath,
output: outputPath
}
I then converted the constructor function to a string, since node child processes don't allow functions to be passed in, only objects, as far as I could tell.
scripts.js:
let configConstructor = require( webpackConfigDir ).constructor
let entry = require( webpackConfigDir ).entry
let output = require( webpackConfigDir ).output
let processOptions = {
constructor: configConstructor.toString(),
entry: entry,
output: output
}
process.send( processOptions )
Within the child process being ran, I converted the string back into a function that gets called once returned.
build.js:
const webpack = require("webpack");
const chalk = require('chalk');
async function build(config) {
await webpack( config, (err, stats) => {
if ( stats.hasErrors() ) {
const errors = stats.toJson().errors
errors.forEach( (error, i) => {
console.log( chalk.white.bgRed.bold(`Error #${i + 1}: `) + chalk.white.bgRed(`${error}`) )
})
}
})
}
process.on("message", async options => {
const constructor = new Function( 'return ' + options.constructor )();
const config = await constructor( options.entry, options.output )
await build(config);
});
This solution worked for the problem at hand, figured I would share in the event anybody runs into a similar problem. In this case, I was trying to share project dependencies but allow a different webpack configuration on a per-directory basis.
Is there a way in webpack to restrict what files can be imported?
Say I want to be able to import files that are in the same directory, as well as the parent directory, but nothing above that parent directory? For example:
These work
import { blah } from "./script.js";
import { blah2 } from "./../gui/textbox.js";
import { blah3 } from "./../I/can/go/as/deep/down/as/I/want/here.js";
But this wouldn't work
import { passwords } from "./../../passwords.txt";
Because that would go up two (or x number of) directories, instead of just one.
You can create a loader to restrict webpack imports to specific files.
// file: webpack.config.js
const path = require('path');
module.exports = {
...
module: {
rules: [
{
test: /\.js$/,
use: [
{
loader: path.resolve('my-webpack-loader.js'),
options: {/* ... */}
}
]
}
]
}
};
Then throw if the resource file is outside ./src and ./node_modules directory or any directory of your choice.
// file: my-webpack-loader.js
const { getOptions } = require('loader-utils');
const validateOptions = require('schema-utils');
const path = require('path');
const schema = {
type: 'object',
properties: {
test: {
type: 'string'
}
}
};
function handler(source) {
const options = getOptions(this);
if(this.resourcePath.indexOf(path.resolve('./node_modules')) !== 0) {
if(this.resourcePath.indexOf(path.resolve('./src')) !== 0) {
throw `Reseource loading restricted for ${this.resourcePath}`;
}
}
validateOptions(schema, options, 'My Webpack Loader');
return source;
}
module.exports = handler;
For more info see writing a webpack loader.
react-dev-utils has a plugin for this.
This Webpack plugin ensures that relative imports from app's source
directories don't reach outside of it.
var path = require('path');
var ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
module.exports = {
// ...
resolve: {
// ...
plugins: [
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
// ...
],
// ...
},
// ...
};
I am building a basic blog project to practice using React, ES6, and Mocha test framework. I'm having trouble transpiling my ES6 tests and app code within my default Gulp task.
I get this error when I run the default task and change the contents of ./test/posts.js for the watch to take effect:
[11:17:29] Using gulpfile ~/WebstormProjects/blog/gulpfile.js
[11:17:29] Starting 'default'...
[11:17:29] Finished 'default' after 8.54 ms
stream.js:75
throw er; // Unhandled stream error in pipe.
^
Error: invalid argument
at pathToArray (/Users/christian/WebstormProjects/blog/node_modules/memory-fs/lib/MemoryFileSystem.js:44:10)
at MemoryFileSystem.mkdirpSync (/Users/christian/WebstormProjects/blog/node_modules/memory-fs/lib/MemoryFileSystem.js:139:13)
at MemoryFileSystem.(anonymous function) [as mkdirp] (/Users/christian/WebstormProjects/blog/node_modules/memory-fs/lib/MemoryFileSystem.js:279:34)
at Compiler.<anonymous> (/Users/christian/WebstormProjects/blog/node_modules/webpack/lib/Compiler.js:229:25)
at Compiler.applyPluginsAsync (/Users/christian/WebstormProjects/blog/node_modules/tapable/lib/Tapable.js:60:69)
at Compiler.emitAssets (/Users/christian/WebstormProjects/blog/node_modules/webpack/lib/Compiler.js:226:7)
at Watching.<anonymous> (/Users/christian/WebstormProjects/blog/node_modules/webpack/lib/Compiler.js:54:18)
at /Users/christian/WebstormProjects/blog/node_modules/webpack/lib/Compiler.js:403:12
at Compiler.next (/Users/christian/WebstormProjects/blog/node_modules/tapable/lib/Tapable.js:67:11)
at Compiler.<anonymous> (/Users/christian/WebstormProjects/blog/node_modules/webpack/lib/CachePlugin.js:40:4)
Process finished with exit code 1
webpack.config.js
var path = require('path');
var babel = require('babel-loader');
module.exports = {
entry: {
app: './app/js/blog.js',
test: './test/posts.js'
},
output: {
filename: '[name].bundle.js',
path: './build',
sourceMapFilename: '[name].bundle.map'
},
watch: true,
devtool: '#sourcemap',
module: {
loaders: [
{
loader: 'babel',
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
query: {
presets: ['react', 'es2015']
}
}
],
resolve: {
root: path.resolve('./app'),
extensions: ['', '.js']
}
}
};
gulpfile.js
var gulp = require('gulp');
var webpack = require('webpack-stream');
var watch = require('gulp-watch');
var babel = require('babel-loader');
var named = require('vinyl-named');
gulp.task('default', function() {
watch(['./app/**/*.js', './test/*.js'], function() {
return gulp.src(['./app/js/blog.js', './test/posts.js'])
.pipe(named())
.pipe(webpack(require('./webpack.config.js')))
.pipe(gulp.dest('./build'));
});
});
gulp.task('testBundle', function() {
gulp.src('./test/posts.js')
.pipe(webpack(require('./webpack.config.js')))
.pipe(gulp.dest('./build'));
});
posts.js
import expect from 'expect'
import { post, posts, addPost } from '../app/js/blog'
import { createStore } from 'redux'
describe('Blog', () => {
describe('posts', () => {
it('should be able to create a post', () => {
let store = createStore(posts);
store.dispatch(addPost('First Post', 'Blah blah blah'))
let blah = { id: 'First Post', content: 'Blah blah blah'}
expect(store.getState()).toEqual(blah)
});
it('should be able to create multiple posts', () => {
let store2 = createStore(posts);
store2.dispatch(addPost('Second Post', 'Shh'))
let expectedState1 = { id: 'Second Post', content: 'Shh' }
expect(store2.getState()).toEqual(expectedState1)
store2.dispatch(addPost('Third Post', 'Whatever'))
let expectedState2 = { id: 'Third Post', content: 'Whatever'}
expect(store2.getState()).toEqual(expectedState2)
})
});
});
Ultimately, I'd like the transpiled code to be found at ./build/blog.bundle.js and ./build/posts.bundle.js for ./app/js/blog.js and ./test/posts.js, respectively.
There were some issues with my webpack.config.js and gulpfile.js. Apparently, the path property within the output object in webpack.config.js was conflicting with gulp.dest('./build'). I also reformatted some things in the config file to mirror a working one. Below is the code that should work. Hopefully this helps others trying to accomplish the same thing.
I have gulp starting webpack to produce separate bundle files for app and test entry points. I also get sourcemaps for each of the bundles that are created. Now I can write tests and app code in ES6 and run them with Mocha within WebStorm!
gulpfile.js
var gulp = require('gulp');
var webpack = require('webpack-stream');
var watch = require('gulp-watch');
gulp.task('default', function() {
watch(['./app/**/*.js', './test/*.js'], function() {
return gulp.src(['./app/js/blog.js', './test/posts.js'])
.pipe(named())
.pipe(webpack(require('./webpack.config.js')))
.pipe(gulp.dest('./build'));
});
});
gulp.task('webpack', function() {
return gulp.src(['./app/js/blog.js', './test/posts.js'])
.pipe(named())
.pipe(webpack(require('./webpack.config.js')))
.pipe(gulp.dest('./build'));
});
webpack.config.js
var path = require('path');
var babel = require('babel-loader');
module.exports = {
entry: {
app: './app/js/entry.js',
test: './test/posts.js'
},
output: {
filename: '[name].bundle.js',
sourceMapFilename: '[name].bundle.map'
},
devtool: '#source-map',
module: {
loaders: [
{
loader: 'babel',
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
query: {
presets: ['react', 'es2015']
}
}
]
},
resolve: {
root: path.resolve('./app'),
extensions: ['', '.js']
}
};
entry.js
import { posts } from './blog'
import { createStore } from 'redux'
createStore(posts)
blog.js
const posts = (state = [], action) => {
switch (action.type) {
case 'ADD_POST':
return post(undefined, action)
default:
return state
}
}
const post = (state = {}, action) => {
switch (action.type) {
case 'ADD_POST':
return { id: action.name, content: action.content }
}
}
// action creator
const addPost = (name, content) => {
return {
type: 'ADD_POST',
name,
content
}
}
export { posts, post, addPost }
I am using webpack to bundle/transform jsx.
From the command line I'm running "webpack --watch". This creates my bundle without errors. Here's my webpack config and Application.js
'use strict';
var webpack = require('webpack');
module.exports = {
resolve: {
extensions: ['', '.js']
},
devtool: 'eval',
entry: './client.js',
output: {
path: __dirname+'/build/js',
filename: 'client.js'
},
module: {
loaders: [
{ test: /\.css$/, loader: 'style!css' },
{ test: /\.js$/, loader: 'jsx-loader?harmony' }
]
}
};
var React = require('react'),
classSet = require('react/addons'),
Nav = require('./Nav.js'),
Home = require('./Home.js'),
Recipe = require('./Recipe.js'),
RecipeArchive = require('./RecipeArchive.js'),
About = require('./About.js'),
Timestamp = require('./Timestamp.js'),
RouterMixin = require('flux-router-component').RouterMixin;
var Application = React.createClass({
mixins: [RouterMixin],
getInitialState: function () {
this.store = this.props.context.getStore('ApplicationStore');
return this.store.getState();
},
componentDidMount: function () {
var self = this;
self._changeEventListener = function () {
var state = self.store.getState();
self.setState(state);
};
self.store.on('change', self._changeEventListener);
},
componentWillUnmount: function () {
var self = this;
self.store.removeListener('change', self._changeEventListener);
self._changeEventListener = null;
},
render: function () {
return (
<div>test</div>
);
}
});
module.exports = Application;
Then I'm running my node server which throws an error.
node server.js
results in:
/Users//Documents/routing/components/Application.js:39
<div>test</div>
^
SyntaxError: Unexpected token <
How should I be running my project to allow me to include jsx/harmony in my .js files?
UPDATE: Solution
As Brandon pointed out, I needed to use node-jsx for transforms in Node. At the top I've of my server.js file I've added and alls working:
require('node-jsx').install({ extension: '.js', harmony: true });
Webpack just creates a client-side bundle; when you run your Node app, it doesn't use webpack to load the files. Prevously you could use something like node-jsx to require JSX files in Node.js, but that project has since been deprecated in favor of Babel. In particular, you can use babel-register to require JSX files in Node.