I want to build a SPA with javascript knockout components
After lots of reading and fiddling I still can't seem to get a working javascript(no typescript) knockout( with components) project with webpack.
I found simple knockout projects but can't get them working with webpack.
Does someone have a demo project wit at least one ko component using webpack?
The Yeoman generator-ko-spa (in javascript) working with Webpack would be great.
Thnx
Here's how to set up a "Hello world" app from scratch:
Installing packages
Create a new folder
Run npm init -y
Install webpack related modules:
npm install --save-dev webpack webpack-cli html-loader
For intellisense in your editor, install knockout
npm install --save-dev knockout
Create a npm command in the scripts section:
"scripts": { "build": "webpack" }
Configuring webpack
Create a webpack.config.js file:
const path = require("path");
module.exports = {
entry: path.resolve(__dirname, "index.js"),
module: {
rules: [
// This tells webpack to import required html files
// as a string, through the html-loader
{ test: /\.html$/, use: [ "html-loader" ] }
],
},
// You *could* include knockout in your bundle,
// but I usually get it from a CDN
externals: {
knockout: "ko"
}
}
Creating our component viewmodel & view
Create a folder named Components
Create Greeter.html
<h1 data-bind="text: message">...</h1>
Create Greeter.js
const greeterTemplate = require("./Greeter.html");
module.exports = {
viewModel: function(params) {
this.message = params.message;
},
template: greeterTemplate
};
Creating our entry points
Create an index.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Document</title>
</head>
<body>
<greeter params="message: 'Hello world!'"></greeter>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="dist/main.js"></script>
</body>
</html>
Create an index.js file
const ko = require("knockout");
const greeter = require("./Components/Greeter");
ko.components.register("greeter", greeter);
ko.applyBindings({});
Build & browser
run npm run build, webpack will create a file in a dist folder
Open index.html in your browser. It should greet you with a "Hello world"!
Related
I have project which uses lerna ( monorepo, multiple packages ). Few of the packages are standalone apps.
What I want to achieve is having aliases on few of the packages to have something like dependency injection. So for example I have alias #package1/backendProvider/useCheckout and in webpack in my standalone app I resolve it as ../../API/REST/useCheckout . So when I change backend provider to something else I would only change it in webpack.
Problem
Problem appears when this alias is used by some other package ( not standalone app ). For example:
Directory structure looks like this:
Project
packageA
ComponentA
packageB
API
REST
useCheckout
standalone app
ComponentA is in packageA
useCheckout is in packageB under /API/REST/useCheckout path
ComponentA uses useCheckout with alias like import useCheckout from '#packageA/backendProvider/useCheckout
Standalone app uses componentA
The error I get is that Module not found: Can't resolve '#packageA/backendProvider/useCheckout
However when same alias is used in standalone app ( that has webpack with config provided below ) it is working. Problem occurs only for dependencies.
Potential solutions
I know that one solution would be to compile each package with webpack - but that doesn't really seem friendly. What I think is doable is to tell webpack to resolve those aliases to directory paths and then to recompile it. First part ( resolving aliases ) is done.
Current code
As I'm using NextJS my webpack config looks like this:
webpack: (config, { buildId, dev, isServer, defaultLoaders }) => {
// Fixes npm packages that depend on `fs` module
config.node = {
fs: "empty"
};
const aliases = {
...
"#package1/backendProvider": "../../API/REST/"
};
Object.keys(aliases).forEach(alias => {
config.module.rules.push({
test: /\.(js|jsx)$/,
include: [path.resolve(__dirname, aliases[alias])],
use: [defaultLoaders.babel]
});
config.resolve.alias[alias] = path.resolve(__dirname, aliases[alias]);
});
return config;
}
You don’t need to use aliases. I have a similar setup, just switch to yarn (v1) workspaces which does a pretty smart trick, it adds sym link to all of your packages in the root node_modules.
This way, each package can import other packages without any issue.
In order to apply yarn workspaces with lerna:
// lerna.json
{
"npmClient": "yarn",
"useWorkspaces": true,
"packages": [
"packages/**"
],
}
// package.json
{
...
"private": true,
"workspaces": [
"packages/*",
]
...
}
This will enable yarn workspace with lerna.
The only think that remains to solve is to make consumer package to transpile the required package (since default configs of babel & webpack is to ignore node_module transpilation).
In Next.js project it is easy, use next-transpile-modules.
// next.config.js
const withTM = require('next-transpile-modules')(['somemodule', 'and-another']); // pass the modules you would like to see transpiled
module.exports = withTM();
In other packages that are using webpack you will need to instruct webpack to transpile your consumed packages (lets assume that they are under npm scope of #somescope/).
So for example, in order to transpile typescript, you can add additional module loader.
// webpack.config.js
{
...
module: {
rules: [
{
test: /\.ts$/,
loader: 'ts-loader',
include: /[\\/]node_modules[\\/]#somescope[\\/]/, // <-- instruct to transpile ts files from this path
options: {
allowTsInNodeModules: true, // <- this a specific option of ts-loader
transpileOnly: isDevelopment,
compilerOptions: {
module: 'commonjs',
noEmit: false,
},
},
}
]
}
...
resolve: {
symlinks: false, // <-- important
}
}
If you have css, you will need add a section for css as well.
Hope this helps.
Bonus advantage, yarn workspaces will reduce your node_modules size since it will install duplicate packages (with the same semver version) once!
Let's say I'm using webpack 4 and have a project that looks like this:
/src/index.js
const Important = "Important Text"
export default Important
global.important = Important
I compile it using the following webpack confing:
output: {
libraryTarget: 'commonjs2',
},
module: {
rules: [
{ ...babelConfig }
]
}
And the package.json has:
{
...packageJsonContents
"name": "important-project",
"main": "./dist/main.js",
}
This will create the minified file /dist/main.js
I save that plugin to npm as testproject
Now I create another project, configure Webpack and npm install important-project.
In the /src/index.js:
import Important from 'important-project'
console.log(Important) //prints "Important Text"
This works, however my goal is for this to be a general use plugin and I want to also be able to include it as a script tag in other projects, however when I include it as a script tag, it complains that module is not defined.
<script src="node_modules/important-project/dist/main.js"></script>
What's the conventional approach here? Should I create two builds? One for in html, and another for in webpack?
I am trying to write a small FOSS UI component library for the Phaser.io game engine. The code lives here. In the src/ dir I have a bunch of components that are exported in the index.js file. These components are bundled using the webpack.config.js and exported to build/phaser-ui.js.
I am having trouble using my built file. Inside the test/ directory, I have created a Phaser Game for testing with the phaser-plus Yeoman generator. My phaser-ui package is installed in the test's package.json from a relative link. This seems to work fine.
The issue I am having is that it appears my phaser-ui file does not have access to the Phaser Library. In the test game I am trying to import a component from the phaser-ui package.json dependency, but this causes the following error to be thrown.
Inside the Game.js state:
import ProgressBar from "phaser-ui";
HTML5 Game Dev Forums post here
Source repo here
In you code, you have, from line 1:
/*
* Star
* ====
*
* Individual star paritcles in the background star emitters
*/
export default class Star extends Phaser.Particle {
// ...
Looks like you haven't imported the library. Phaser is undefined, and so Phaser.Particle is undefined.
Did you forget to const Phaser = require('phaser'); before using Phaser?
Alternatively, you can do import {default as Phaser} from 'phaser'; If you want new syntax, and has set up environment to use it.
K so I got it halfway working (update - works 100%). I changed my test script to properly reinstall the package in the test dir each time:
PHASER-UI's package.json
"scripts": {
"test": "npm run build && cd test && npm install phaser-ui && npm start",
"build": "webpack"
},
The test package installs phaser-ui from a local NPM directory
TEST's package.json
"dependencies": {
"phaser-ce": "^2.7.0",
"phaser-ui": "file:../"
},
Then, I included the 'Phaser' variable as an external in my webpack
var webpack = require('webpack');
module.exports = {
entry: './src/index.js',
output: {
path: 'build/',
filename: 'phaser-ui.js'
},
//needed in my src library when extending/using Phaser objects/code
externals: {
Phaser: 'Phaser'
}
};
So now it builds successfully and seems to work. The issue is that I don't know how to consume it in my game state. Importing and logging it only shows a generic object in the console
import * as lib from 'phaser-ui';
export default class Game extends Phaser.State {
create() {
console.log(lib)
}
}
Update
Turns out I needed to add some library configuration to my webpack.config.js. By default webpack libraries are designed to be "installed" via a scripts tag and consumed via a global variable. I want my library to be a node_module that is consumed by an import componentName from 'phaser-ui';, so I needed to change the libraryTarget to umd.
Final webpack.config.js:
module.exports = {
entry: './src/index.js',
//setup the webpack output as a library
output: {
path: 'build/',
filename: 'phaser-ui.js',
libraryTarget: 'umd', //Honestly not sure of diffs between umd, amd, commonjs, etc (but umd works!)
library: 'phaserUi'
},
//needed in my src library when extending/using Phaser objects/code
externals: {
Phaser: 'Phaser'
}
};
I'm starting VueJS, I started my code from the original Vue Loader Example and I tested running npm run dev or npm run build but a question emerge : Is there a way to place all the css from the components in one place (like styles.min.css).
I added bootstrap as dependencies in my package.json and but when I do a npm run build I can only find the build.js file in dist, that's all.
Thank you for your help.
There's a plugin for that
npm install extract-text-webpack-plugin --save-dev
and then configure it in your webpack.config.js
var ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
// other options...
module: {
loaders: [
{
test: /\.vue$/,
loader: 'vue'
},
]
},
vue: {
loaders: {
css: ExtractTextPlugin.extract("css"),
// you can also include <style lang="less"> or other langauges
less: ExtractTextPlugin.extract("css!less")
}
},
plugins: [
new ExtractTextPlugin("style.css")
]
}
I'm setting up my environment to develop a polymer 1.0 application using ES6/babel.
I also want to integrate webpack, so that I can use import statements in my javascript code.
I use gulp as a build tool. This is a (simplified) gulpfile for my project:
var gulp = require('gulp');
var gulpif = require('gulp-if');
var crisper = require('gulp-crisper');
var webpack = require('gulp-webpack');
gulp.task('default', function() {
gulp.src('src/*.html')
.pipe(gulpif('*.html', crisper()))
.pipe(gulpif('*.js', webpack({
loaders: [
{
test: /\.js$/,
exclude: [/node_modules/],
loader: 'babel-loader?sourceMap=true'
}
]
})))
.pipe(gulp.dest('dist'));
});
This works if I use gulp-babel instead of gulp-webpack. But so I get the following error:
[10:28:38] Using gulpfile E:\Dokumente\polymer\test-gulp-webpack\gulpfile.js
[10:28:38] Starting 'default'...
[10:28:38] Finished 'default' after 225 ms
[10:28:38] Version: webpack 1.12.2
ERROR in Entry module not found: Error: Cannot resolve 'file' or 'directory' E:\Dokumente\polymer\test-gulp-webpack\src\index.js in E:\Dokumente\polymer\test-gulp-webpack
The src directory contains only a index.html with the following content:
<html>
<body>
<h1>TEST</h1>
<script>
var test = "TEST";
</script>
</body>
</html>
Any suggestion how I can fix this? Maybe tell crisper to temporarily write the javascript to the filesystem? Or maybe use some fancy loader for webpack?