My goal is to let a webpack project be setup through node command crateEntireWepbackProject.js file.
I want to execute commands on the shell from a js file, so I can let them run automatically and later on also include custom specifications for a project.
The js file is not from within webpack, but has the commands to create a wepback project from scratch and makes the installs etc. just by me typing node createEntireWebpackProject.js
You do not need to write it from scratch. The best practice is using yeoman. There are a lot of generators with webpack. For example:
const Generator = require('yeoman-generator');
const mkdirp = require('mkdirp');
const path = require('path');
module.exports = class extends Generator {
prompting() {
this.log('Welcome to the classy example generator!');
const prompts = [
{
type: 'input',
name: 'name',
message: 'Name?',
default: this.appname,
}];
return this.prompt(prompts).then((props) => {
this.props = props;
});
}
default() {
if (path.basename(this.destinationPath()) !== this.props.name) {
this.log(
`Your application must be inside a folder named ${this.props.name}`);
this.log('I\'ll automatically create this folder.');
mkdirp(this.props.name);
this.destinationRoot(this.destinationPath(this.props.name));
}
}
writing() {
this.createPackageJson();
this.copyFiles();
this.fillTemplates();
this.makeCommands();
}
install() {
this.npmInstall(['bunyan', 'dotenv-safe'], { save: true });
this.npmInstall(['eslint', 'eslint-config-airbnb-base', 'eslint-plugin-import', 'jest'], { 'save-dev': true });
}
createPackageJson() {
this.fs.extendJSON('package.json', {
name: this.props.name,
version: '0.1.0',
main: 'src/app.js',
scripts: {
cs: 'eslint src/* __tests__/*',
'cs:fix': 'eslint src/* __tests__/* --fix',
start: 'node src/app.js',
test: 'npm run cs && jest',
},
dependencies: {},
devDependencies: {},
engines: {
node: '^8.1.0',
},
private: true,
jest: {
testEnvironment: 'node',
transform: {},
collectCoverage: true,
},
});
}
copyFiles() {
[
'.dockerignore',
'.eslintrc.json',
'src/app.js',
].forEach(name => this.fs.copy(this.templatePath(name), this.destinationPath(name)));
}
fillTemplates() {
this.fs.copyTpl(
this.templatePath('README.md'),
this.destinationPath('README.md'),
{
name: this.props.name,
});
}
makeCommands() {
this.spawnCommandSync('git' ['init']);
this.spawnCommandSync('git', ['add', '.']);
this.spawnCommandSync('git', ['commit', '-am', '"yo scaffolded app"']);
}
};
Related
I am not really strong with setting up my custom npm packages, therefore I am asking for help. I am trying to test my custom ESLint plugin rules on another project. For this, I have set up two projects in one root directory and linked them, using the file linking option (find the image of the file structure attached):
"eslint-plugin-winniepukki-guidelines": "file:../guide"
.eslintrc configuration file on the project, where I want to use my custom ESLint plugin:
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: [
'airbnb-base',
],
parserOptions: {
ecmaVersion: 12,
sourceType: 'module',
},
plugins: [
'winniepukki-guidelines',
],
rules: {
'winniepukki-guidelines/use-license': 'error',
'winniepukki-guidelines/avoid-names': 'error',
},
};
Unfortunately, the rules do not apply properly with the following error:
Definition for rule 'winniepukki-guidelines/avoid-names' was not found.eslint(winniepukki-guidelines/avoid-names). This is how the avoid-names.js file looks like:
module.exports = {
meta: {
messages: {
avoidName: "Avoid using variables named '{{ name }}'"
}
},
create(context) {
return {
Identifier(node) {
if (node.name === "foo") {
context.report({
node,
messageId: "avoidName",
data: {
name: "foo",
}
});
}
}
};
}
};
And this is the ESLint package's index.js file itself, illustrating how do I export them:
const path = require('path');
const requireIndex = require('requireindex');
module.exports.rules = requireIndex(path.join(__dirname, 'rules'));
Project overview: https://i.stack.imgur.com/hLKcH.png
I'm trying to mock the react-native-image-crop-picker package (the openCamera function specifically) in Detox but I'm not managing to do it. Detox simply does not consider the mock.
.detoxrc.json:
{
"testRunner": "jest",
"runnerConfig": "e2e/config.json",
"uiHierarchy": "enabled",
"configurations": {
"android.emu.debug": {
"binaryPath": "android/app/build/outputs/apk/debug/app-debug.apk",
"build": "yarn detox:prepare",
"type": "android.emulator",
"device": {
"avdName": "Pixel_4_API_29"
}
}
}
}
.config.json:
{
"preset": "ts-jest",
"testEnvironment": "./environment",
"testRunner": "jest-circus/runner",
"testTimeout": 120000,
"reporters": ["detox/runners/jest/streamlineReporter"],
"verbose": true,
"testMatch": [
"**/__tests__/**/*.js?(x)",
"**/?(*.)(e2e).js?(x)",
"**/__tests__/**/*.ts?(x)",
"**/?(*.)(e2e).ts?(x)"
]
}
config.js:
require('#babel/register')({
//cache: true,
presets: [require('metro-react-native-babel-preset')],
plugins: [require('#babel/plugin-transform-runtime').default],
only: ['./e2e', './ts', './js'],
ignore: ['node_modules'],
});
environment.js:
const {
DetoxCircusEnvironment,
SpecReporter,
WorkerAssignReporter,
} = require('detox/runners/jest-circus');
class CustomDetoxEnvironment extends DetoxCircusEnvironment {
constructor(config) {
super(config);
// Can be safely removed, if you are content with the default value (=300000ms)
this.initTimeout = 300000;
// This takes care of generating status logs on a per-spec basis. By default, Jest only reports at file-level.
// This is strictly optional.
this.registerListeners({
SpecReporter,
WorkerAssignReporter,
});
}
}
module.exports = CustomDetoxEnvironment;
metro.config.js:
const blacklist = require('metro-config/src/defaults/blacklist');
const defaultSourceExts = require('metro-config/src/defaults/defaults')
.sourceExts;
/**
* Metro configuration for React Native
* https://github.com/facebook/react-native
*
* #format
*/
module.exports = {
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false,
},
}),
},
/**
*
* Blacklist is a function that takes an array of regexes
* and combines them with the default blacklist to return a single regex.
*
*/
resolver: {
blacklistRE: blacklist([/.\/amplify\/.*/]),
sourceExts: process.env.RN_SRC_EXT
? process.env.RN_SRC_EXT.split(',').concat(defaultSourceExts)
: defaultSourceExts,
},
};
E2E folder:
E2E Folder
ImageCropPicker.e2e.js:
import ImageCropPicker from 'react-native-image-crop-picker';
ImageCropPicker.openCamera = function openCamera() {
console.tron.log('mocked');
return {
mime: 'test',
data: 'test',
};
};
I also tried to put the ImageCropPicker.e2e.js file outside the mocks folder, but it did not work as well.
Detox, Node, Device and OS versions:
Detox: 17.6.0
Node: 10.23.0
Device: Pixel 4 API 29
OS: Linux Pop!_OS 20.10
React-Native: 0.62.0
Can you help me?
I appreciate your time!
I'm using eleventy to create a static site with a sprinkle of JavaScript. I'm not using webpack or other bundlers. JavaScript is transpiled by calling 'transformFileAsync' via eleventys beforeBuild event. Here's the relevant part of eleventy config:
babel.transformFileAsync("src/assets/js/index.js").then((result) => {
fs.outputFile("dist/assets/main.js", result.code, (err) => {
if (err) throw err;
console.log("JS transpiled.");
});
});
My babel.config.js is as follows:
module.exports = (api) => {
api.cache(true);
const presets = [
[
"#babel/preset-env",
{
bugfixes: true,
modules: "systemjs",
useBuiltIns: "usage",
corejs: { version: 3, proposals: true },
},
],
];
const plugins = [];
return { presets, plugins };
};
Babel works as advertised and transpiles my js just fine. But I can't figure out how I can include (without help from a bundler) corejs polyfills in the final production bundle.
For example, the following code:
Array.from(document.getElementsByTagName("p")).forEach((p) => {
console.log(`p ${index}, startsWith('W')`, p, p.innerHTML.startsWith("W"));
});
Is transpiled to:
import "core-js/modules/es.array.for-each";
import "core-js/modules/es.array.from";
import "core-js/modules/es.string.iterator";
import "core-js/modules/es.string.starts-with";
import "core-js/modules/web.dom-collections.for-each";
System.register([], function (_export, _context) {
"use strict";
return {
setters: [],
execute: function () {
Array.from(document.getElementsByTagName("p")).forEach(function (p) {
console.log("p ".concat(index, ", startsWith('W')"), p, p.innerHTML.startsWith("W"));
});
}
};
});
How would I go about having the actual polyfill in the final bundle instead of all the imports?
I recently tried to codesplit my svelte web app for each page, but I haven't been able to get it to work while using the crypto-js package. If i remove the package everything works. The js compiles with the line import "crypto", and that causes the browser to error and not to work.
rollup.config.js
import resolve from '#rollup/plugin-node-resolve';
import commonjs from '#rollup/plugin-commonjs';
import livereload from 'rollup-plugin-livereload';
import json from '#rollup/plugin-json';
import { config } from "dotenv"
import replace from '#rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
const production = !process.env.ROLLUP_WATCH;
function serve() {
let server;
function toExit() {
if (server) server.kill(0);
}
return {
writeBundle() {
if (server) return;
server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
stdio: ['ignore', 'inherit', 'inherit'],
shell: true
});
process.on('SIGTERM', toExit);
process.on('exit', toExit);
}
};
}
let envVar = {}
Object.entries(config().parsed).map(([prop, value]) => {
if (prop.startsWith("APP")) envVar[prop] = value
});
export default {
input: 'src/main.js',
output: {
sourcemap: !production,
format: 'esm',
name: 'app',
dir: 'public/build',
},
plugins: [
json(),
replace({
__myapp: JSON.stringify({
env: envVar
}),
}),
svelte({
// enable run-time checks when not in production
dev: !production,
// we'll extract any component CSS out into
// a separate file - better for performance
css: css => {
css.write('bundle.css');
}
}),
// If you have external dependencies installed from
// npm, you'll most likely need these plugins. In
// some cases you'll need additional configuration -
// consult the documentation for details:
// https://github.com/rollup/plugins/tree/master/packages/commonjs
commonjs({
transformMixedEsModules:true,
sourceMap: !production,
}),
resolve({
browser: true,
dedupe: ['svelte'],
preferBuiltins: false
}),
// In dev mode, call `npm run start` once
// the bundle has been generated
!production && serve(),
// Watch the `public` directory and refresh the
// browser on changes when not in production
!production && livereload('public'),
// If we're building for production (npm run build
// instead of npm run dev), minify
production && terser()
],
watch: {
clearScreen: false
}
};
Browser Error
Uncaught TypeError: Failed to resolve module specifier "crypto". Relative references must start with either "/", "./", or "../".
main.js
(function(l, r) { if (l.getElementById('livereloadscript')) return; r = l.createElement('script'); r.async = 1; r.src = '//' + (window.location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1'; r.id = 'livereloadscript'; l.getElementsByTagName('head')[0].appendChild(r) })(window.document);
export { ao as default } from './main-d2838af7.js';
import 'crypto';
//# sourceMappingURL=main.js.map
Using crypto-js should just work.
If you download a ZIP from an empty REPL and just add the crypto-js package, it will be compiled without any import "crypto"; statements. There is an internal fallback mechanism that will resolve to using the the browser-native crypto module.
/**
* CryptoJS core components.
*/
var CryptoJS = CryptoJS || (function (Math, undefined$1) {
var crypto;
// Native crypto from window (Browser)
if (typeof window !== 'undefined' && window.crypto) {
crypto = window.crypto;
}
// ...
I tested it with a dynamic import and ESM output as well:
<!-- App.svelte -->
<div class="content">
{#await import('crypto-js') then cryptoJs}
{cryptoJs.default.SHA256('hello')}
{/await}
</div>
Adjusted config for ESM and to support chunks (requires output directory):
import svelte from 'rollup-plugin-svelte';
import commonjs from '#rollup/plugin-commonjs';
import resolve from '#rollup/plugin-node-resolve';
import livereload from 'rollup-plugin-livereload';
import { terser } from 'rollup-plugin-terser';
import css from 'rollup-plugin-css-only';
const production = !process.env.ROLLUP_WATCH;
function serve()
{
let server;
function toExit()
{
if (server) server.kill(0);
}
return {
writeBundle()
{
if (server) return;
server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
stdio: ['ignore', 'inherit', 'inherit'],
shell: true
});
process.on('SIGTERM', toExit);
process.on('exit', toExit);
}
};
}
/** #type {import('rollup').RollupOptions} */
export default {
input: {
bundle: 'src/main.js',
},
output: {
sourcemap: true,
format: 'esm',
name: 'app',
dir: 'public/build',
},
plugins: [
svelte({
compilerOptions: {
dev: !production
}
}),
css({ output: 'bundle.css' }),
resolve({
browser: true,
dedupe: ['svelte'],
}),
commonjs(),
!production && serve(),
!production && livereload('public'),
production && terser()
],
watch: {
clearScreen: false
},
};
Vue Cli defaults to file-loader for SVG assets, but I want to use svg-sprite-loader (as well as a few others) instead.
I updated the vue.config.js file to do this and it still seems to use file-loader. Almost as though it's not picking up my config at all.
vue.config.js
module.exports = {
configureWebpack: {
module: {
rules: [
{
test: /\.(svg)(\?.*)?$/,
use: [
{
loader: 'svg-sprite-loader',
options: {
name: '[name]-[hash:7]',
prefixize: true
}
},
'svg-fill-loader',
'svgo-loader'
]
}
]
}
}
}
Is there anything wrong with my setup?
I'm still getting SVG files imported into my component as a URL string / path when it should be an object with properties.
Many thanks.
This took me a while to find a work around. Basically you need to stop file-loader matching on .svg. The best way I have found to do this is using chainWebpack and returning false from the test method on file-loader. I have included my working config.
module.exports = {
lintOnSave: false,
configureWebpack: {
module: {
rules: [
{
test: /\.(svg)(\?.*)?$/,
use: [
{
loader: 'svg-inline-loader',
options: {
limit: 10000,
name: 'assets/img/[name].[hash:7].[ext]'
}
}
]
}
]
}
},
chainWebpack: config => {
config.module
.rule('svg')
.test(() => false)
.use('file-loader')
}
}
The Webpack docs for Vue CLI 3.0 beta got updated with an example on how to replace an existing Base Loader. For svg-sprite-loader this means that you'll have to add the following configuration to your vue.config.js:
chainWebpack: config => {
config.module
.rule('svg')
.use('file-loader')
.loader('svg-sprite-loader')
}
I'm using Vue CLI 3.0.3 and this config works for me 😉
const path = require('path');
const glob = require('glob');
const SpriteLoaderPlugin = require('svg-sprite-loader/plugin');
module.exports = {
lintOnSave: false,
configureWebpack: {
plugins: [
new SpriteLoaderPlugin()
]
},
chainWebpack: config => {
config.module.rules.delete('svg');
config
.entry('app')
.clear()
.add(path.resolve(__dirname, './src/main.ts'))
config
.entry('sprite')
.add(...glob.sync(path.resolve(__dirname, `./src/assets/icons/*.svg`)));
config.module.rule('svg')
.test(/\.(svg)(\?.*)?$/)
.use('file-loader')
.loader('svg-sprite-loader')
.options({
extract: true,
spriteFilename: 'icons.svg'
})
}
};
Vue CLI docs for version 3.x in webpack section suggests to use something like this:
// vue.config.js
module.exports = {
chainWebpack: config => {
const svgRule = config.module.rule('svg')
// clear all existing loaders.
// if you don't do this, the loader below will be appended to
// existing loaders of the rule.
svgRule.uses.clear()
// add replacement loader(s)
svgRule
.use('vue-svg-loader')
.loader('vue-svg-loader')
}
}
Even vue-svg-loader configuration guide suggests same approach.
module.exports = {
chainWebpack: config => {
const svgRule = config.module.rule('svg')
svgRule.clear()
svgRule
.use('vue-svg-loader')
.loader('vue-svg-loader')
}
}