I am working on a project consisting of three parts: a Client, a Server and a Common directory which contains things I want to import from both the Client and the Server. Everything can use both JS and TS. (Thanks to the babel-typescript preset)
Directory structure
Here is how it looks like:
root/
├── babel.config.js
├── Common/
│ ├── helper1.ts
│ ├── helper2.ts
│ ├── helper3.js
├── Client/
│ ├── src/
│ │ └── file1.js
│ └── .babelrc.js
└── Server/
├── src/
│ └── file1.js
└── .babelrc.js
Babel config files
Here is what my root/babel.config.js looks like:
module.exports = {
presets: ["#babel/preset-typescript"],
plugins: [
["#babel/plugin-transform-for-of", { assumeArray: true }],
"#babel/plugin-syntax-dynamic-import",
"#babel/plugin-syntax-import-meta",
"#babel/plugin-proposal-class-properties",
"#babel/plugin-proposal-json-strings",
["#babel/plugin-proposal-decorators", { legacy: true }],
"#babel/plugin-proposal-function-sent",
"#babel/plugin-proposal-export-namespace-from",
"#babel/plugin-proposal-numeric-separator",
"#babel/plugin-proposal-throw-expressions",
"#babel/plugin-proposal-export-default-from",
"#babel/plugin-proposal-logical-assignment-operators",
"#babel/plugin-proposal-optional-chaining",
["#babel/plugin-proposal-pipeline-operator", { proposal: "minimal" }],
"#babel/plugin-proposal-nullish-coalescing-operator",
"#babel/plugin-proposal-do-expressions",
"#babel/plugin-proposal-function-bind",
],
};
And here is what my Server/.babelrc.js looks like:
const moduleAlias = require("./tools/module-alias");
const rootConfig = require("../babel.config");
module.exports = {
presets: [
...rootConfig.presets,
[
"#babel/preset-env",
{
targets: {
node: "current",
},
exclude: ["transform-for-of"],
},
],
],
plugins: [
...rootConfig.plugins,
[
"babel-plugin-module-resolver",
{
root: ["."],
alias: moduleAlias.relativeAliases,
extensions: [".js", ".ts"],
},
],
],
};
I will omit the Client/.babelrc.js since it's very similar to the Server one.
Basic test files
Here is an example Common/helper3.js file:
function doubleSay(str) {
return `${str}, ${str}`;
}
function capitalize(str) {
return str[0].toUpperCase() + str.substring(1);
}
function exclaim(str) {
return `${str}!`;
}
const result = "hello" |> doubleSay |> capitalize |> exclaim;
console.log(result);
And inside Server/src/index.js I just import the file Common/helper3.js.
The error
Then, inside the Server directory, I do this:
npx babel-node src/index.js -x .ts,.js
Which prints the following error:
const result = "hello" |> doubleSay |> capitalize |> exclaim;
^
SyntaxError: Unexpected token >
I am definitely sure this error is related to my "strange" directory structure since it's fine when I put this exact file under Server/src.
The question
How can I keep this directory structure and tell Babel to use a config when it processes files within the Common directory?
I don't use Lerna or anything. I have setup special aliases that resolve $common to ../Common where needed. I know there is no issue with this since the file is properly found by Babel (otherwise I would get a "File not found" error)
Note
This babel structure is one of my attempt to fix the issue above. Originally I only had one babel.config.js inside Server and another inside Client. I thought having one at the root would solve this problem but it didn't change anything.
Edit after searching a lot more:
After taking a look at the babel code to find the config parsing, I noticed that this line : https://github.com/babel/babel/blob/8ca99b9f0938daa6a7d91df81e612a1a24b09d98/packages/babel-core/src/config/config-chain.js#L456 is called (null is returned).
I printed everything in this scope and noticed that babel automatically generates an only parameter containing the cwd. (Effectively saying that my babel.config.js doesn't affect my common directory despite being "above" is in the directory hierarchy).
I decided to try overloading it in the command line and arrived at this command:
npx babel-node src/index.js --root-mode upward -x .ts,.js --only .,../Common/ --ignore node_modules
(Added --only and --ignore)
This made me progress a bit: instead of failing to parse advanced syntax (pipeline operator) in js files, it failed on a ts failing, saying
export const accountStatus = Object.freeze({
^^^^^^
SyntaxError: Unexpected token export
What I don't understand is how it can parse the pipeline operator but not the typescript file even though both the pipeline plugin and the typescript are inside the same babel.config.js
Edit after solving this last issue:
Adding --only and --ignore made it work. The other issue was because I forgot to add the #babel/plugin-transform-modules-commonjs plugin and it was not able to resolve the import.
The slight change I did was adding ignore: ["**/node_modules"], to my root babel.config.js file and change my command to use those arguments: --root-mode upward -x .ts,.js --ignore __fake__.
Adding a random --ignore is enough to prevent babel from guessing by itself.
This is the solution I use and it works fine even though it's not very elegant.
Related
Based on the Cypress documentation, we can run multiple test files using this syntax
cypress run --spec "cypress/e2e/examples/actions.cy.js,cypress/e2e/examples/files.cy.js"
My question is when I have to run 4 of 10 test files in the same folder i have to define like this,
cypress run --spec "cypress/e2e/**/test1.cy.js,cypress/e2e/**/test2.cy.js,cypress/e2e/**/test3.cy.js,cypress/e2e/**/test4.cy.js"
Can we make simplify it, idk is cypress has a feature for defining the spec folder?
So if we can define the folder test spec path like specFolder = cypress/e2e/**/ and I just write script
cypress run --spec "test1.cy.js,test2.cy.js,test3.cy.js,test4.cy.js"
You can run all tests in a specified directory as documented here https://docs.cypress.io/guides/guides/command-line#cypress-run-spec-lt-spec-gt
So try to run all the tests in a directory like so
cypress run --spec "cypress/e2e/**/*
You can use a a little javascript in cypress.config.js to do it, but instead of --spec option specify the tests in an --env variable.
// cypress.config.js
const { defineConfig } = require("cypress");
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
if (config.env.specs) { // only if specs are specified on CLI
const specBase = config.specPattern.split('*')[0] // === "cypress/e2e/"
// temporary change to specPattern
config.specPattern = config.env.specs.map(spec=> `${specBase}${spec}`)
}
return config
},
specPattern: 'cypress/e2e/**/*.cy.{js,jsx,ts,tsx}'
},
});
Called with script
// package.json
{
"scripts": {
"cy:specs": "npx cypress run --env specs=[test1.cy.js,test2.cy.js]"
}
}
Run output:
specPattern [ 'cypress/e2e/test1.cy.js', 'cypress/e2e/test2.cy.js' ]
=====================================================================================
(Run Starting)
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Cypress: 12.3.0 │
│ Browser: Electron 106 (headless) │
│ Node Version: v18.12.1 (C:\Program Files\nodejs\node.exe) │
│ Specs: 2 found (test1.cy.js, test2.cy.js) │
│ Searched: cypress/e2e/test1.cy.js, cypress/e2e/test2.cy.js │
└─────────────────────────────────────────────────────────────────────────────────┘
Running: test1.cy.js (1 of 2)
etc
OK, i have searched high and low but cannot reliably deterrmine if this is or is not possible with webpack.
https://github.com/webpack/webpack/tree/master/examples/require.context
Appears to indicate that one can pass a string to a function and it load a module...
But my attempt is just not working:
webpack.config.js
'use strict';
let webpack = require('webpack'),
jsonLoader = require("json-loader"),
path = require("path"),
fs = require('fs'),
nodeModules = {};
fs.readdirSync('node_modules')
.filter(function(x) {
return ['.bin'].indexOf(x) === -1;
})
.forEach(function(mod) {
nodeModules[mod] = 'commonjs ' + mod;
});
let PATHS = {
app: __dirname + '/src'
};
module.exports = {
context: PATHS.app,
entry: {
app: PATHS.app+'/server.js'
},
target: 'node',
output: {
path: PATHS.app,
filename: '../build/server.js'
},
externals: nodeModules,
performance: {
hints: "warning"
},
plugins: [
jsonLoader
],
resolve: {
modules: [
'./node_modules',
path.resolve(__dirname),
path.resolve(__dirname + "/src"),
path.resolve('./config')
]
},
node: {
fs: "empty"
}
};
The server.js
let _ = require('lodash');
let modules = [ "modules/test" ];
require( 'modules/test' )();
_.map( modules, function( module ){
require( module );
});
The module in modules/ named test.js
module.exports = () => {
console.log('hello world');
};
But the result is always the same... the pm2 logs just say hello world for the static require... but for the dynamic load of the same module
Error: Cannot find module "."
All i want to be able to do is loop through an array of paths to modules and load then...
You cannot use a variable as argument to require. Webpack needs to know what files to bundle at compile time. As it does no program flow analysis, it can't know what you pass to the function. In that case it might be obvious, but this could go as far as using user input to decide what module to require, and there is no way webpack can possibly know which modules to include at compile time, so webpack does not allow it.
The example you posted is a bit different. You could use require with a concatenated string. For example:
require(`./src/${moduleName}/test`);
Which modules does webpack need to include in the bundle? The variable moduleName could be anything, so the exact module is not known at compile time. Instead it includes all modules that could possibly match the above expression. Assuming the following directory structure:
src
├─ one
│ └─ test.js
├─ two
│ ├─ subdir
│ │ └─ test.js
│ └─ test.js
└─ three
└─ test.js
All of these test.js files will be included in the bundle, because moduleName could be one or something nested like two/subdir.
For more details see require with expression of the official docs.
You cannot loop through an array and import every module of the array, with the above exception by concatenating a string, but that has the effect of including all possible modules and should generally be avoided.
I ran into this problem in an electron environment. My use case was being able to require dynamically created files in an IDE like application. I wanted to use the electron require, which is basically a NodeJS Common module loader. After some back and forth I landed on a solution that uses webpack's noParse module configuration.
First create a module that that will be ignored by webpack's parser:
// file: native-require.js
// webpack replaces calls to `require()` from within a bundle. This module
// is not parsed by webpack and exports the real `require`
// NOTE: since the module is unparsed, do not use es6 exports
module.exports = require
In my webpack config, under module, instruct the bundler not to parse this module:
{
module: {
noParse: /\/native-require.js$/,
}
}
Lastly, in any bundle where you want to access the original require:
import nativeRequire from './native-require'
const someModule = nativeRequire('/some/module.js') // dynamic imports
A bit late....but... since you are bundling to target: 'node', there is a workaround to dynamic requiring modules, and bypassing the "the effect of including all possible modules".
The solution is lifted from:
Using dynamic require on node targets WITHOUT resolve or bundle the target module · Issue #4175 · webpack/webpack
Quoted from that comment:
const requireFunc = typeof __webpack_require__ === "function" ? __non_webpack_require__ : require;
const foo = requireFunc(moduleName);
Bundles to:
const requireFunc = true ? require : require;
const foo = requireFunc(moduleName);
OK, i have searched high and low but cannot reliably deterrmine if this is or is not possible with webpack.
https://github.com/webpack/webpack/tree/master/examples/require.context
Appears to indicate that one can pass a string to a function and it load a module...
But my attempt is just not working:
webpack.config.js
'use strict';
let webpack = require('webpack'),
jsonLoader = require("json-loader"),
path = require("path"),
fs = require('fs'),
nodeModules = {};
fs.readdirSync('node_modules')
.filter(function(x) {
return ['.bin'].indexOf(x) === -1;
})
.forEach(function(mod) {
nodeModules[mod] = 'commonjs ' + mod;
});
let PATHS = {
app: __dirname + '/src'
};
module.exports = {
context: PATHS.app,
entry: {
app: PATHS.app+'/server.js'
},
target: 'node',
output: {
path: PATHS.app,
filename: '../build/server.js'
},
externals: nodeModules,
performance: {
hints: "warning"
},
plugins: [
jsonLoader
],
resolve: {
modules: [
'./node_modules',
path.resolve(__dirname),
path.resolve(__dirname + "/src"),
path.resolve('./config')
]
},
node: {
fs: "empty"
}
};
The server.js
let _ = require('lodash');
let modules = [ "modules/test" ];
require( 'modules/test' )();
_.map( modules, function( module ){
require( module );
});
The module in modules/ named test.js
module.exports = () => {
console.log('hello world');
};
But the result is always the same... the pm2 logs just say hello world for the static require... but for the dynamic load of the same module
Error: Cannot find module "."
All i want to be able to do is loop through an array of paths to modules and load then...
You cannot use a variable as argument to require. Webpack needs to know what files to bundle at compile time. As it does no program flow analysis, it can't know what you pass to the function. In that case it might be obvious, but this could go as far as using user input to decide what module to require, and there is no way webpack can possibly know which modules to include at compile time, so webpack does not allow it.
The example you posted is a bit different. You could use require with a concatenated string. For example:
require(`./src/${moduleName}/test`);
Which modules does webpack need to include in the bundle? The variable moduleName could be anything, so the exact module is not known at compile time. Instead it includes all modules that could possibly match the above expression. Assuming the following directory structure:
src
├─ one
│ └─ test.js
├─ two
│ ├─ subdir
│ │ └─ test.js
│ └─ test.js
└─ three
└─ test.js
All of these test.js files will be included in the bundle, because moduleName could be one or something nested like two/subdir.
For more details see require with expression of the official docs.
You cannot loop through an array and import every module of the array, with the above exception by concatenating a string, but that has the effect of including all possible modules and should generally be avoided.
I ran into this problem in an electron environment. My use case was being able to require dynamically created files in an IDE like application. I wanted to use the electron require, which is basically a NodeJS Common module loader. After some back and forth I landed on a solution that uses webpack's noParse module configuration.
First create a module that that will be ignored by webpack's parser:
// file: native-require.js
// webpack replaces calls to `require()` from within a bundle. This module
// is not parsed by webpack and exports the real `require`
// NOTE: since the module is unparsed, do not use es6 exports
module.exports = require
In my webpack config, under module, instruct the bundler not to parse this module:
{
module: {
noParse: /\/native-require.js$/,
}
}
Lastly, in any bundle where you want to access the original require:
import nativeRequire from './native-require'
const someModule = nativeRequire('/some/module.js') // dynamic imports
A bit late....but... since you are bundling to target: 'node', there is a workaround to dynamic requiring modules, and bypassing the "the effect of including all possible modules".
The solution is lifted from:
Using dynamic require on node targets WITHOUT resolve or bundle the target module · Issue #4175 · webpack/webpack
Quoted from that comment:
const requireFunc = typeof __webpack_require__ === "function" ? __non_webpack_require__ : require;
const foo = requireFunc(moduleName);
Bundles to:
const requireFunc = true ? require : require;
const foo = requireFunc(moduleName);
I'm new with Grunt - csslint plugin, after I run and cssLint task complete, there are many and many errors and warnings that I can't follow. So how to config task just print out the errors, not warning??
If you use grunt-contrib-csslint you can specify the options in a .csslintrc file.
From the grunt-contrib-csslint Readme:
Options
Any specified option will be passed through directly to csslint, thus
you can specify any option that csslint supports. The csslint API is a
bit awkward: For each rule, a value of false ignores the rule, a value
of 2 will set it to become an error. Otherwise all rules are
considered warnings.
Assuming you have a structure like this:
├── .csslintrc
├── Gruntfile.js
├── css
│ └── foo.css
├── node_modules
└── package.json
.csslintrc
{
"ignore": [
"adjoining-classes",
"box-model",
"box-sizing",
"bulletproof-font-face",
"compatible-vendor-prefixes",
"display-property-grouping",
"duplicate-background-images",
"duplicate-properties",
"empty-rules",
"fallback-colors",
"floats",
"font-faces",
"font-sizes",
"gradients",
"ids",
"import",
"import-ie-limit",
"important",
"known-properties",
"non-link-hover",
"order-alphabetical",
"outline-none",
"overqualified-elements",
"qualified-headings",
"regex-selectors",
"rules-count",
"selector-max",
"selector-max-approaching",
"selector-newline",
"shorthand",
"star-property-hack",
"text-indent",
"underscore-property-hack",
"unique-headings",
"universal-selector",
"unqualified-attributes",
"vendor-prefix",
"zero-units"
]
}
reference: https://github.com/CSSLint/csslint/wiki/Command-line-interface
Gruntfile
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
csslint: {
strict: {
src: ['css/*.css']
},
lax: {
options: {
csslintrc: '.csslintrc'
},
src: ['css/*.css']
}
}
});
grunt.loadNpmTasks('grunt-contrib-csslint');
grunt.registerTask('default', ['csslint:lax']);
};
Then grunt will report only errors and grunt csslint:strict will report warnings and errors.
Note: This question is only relevant for Grunt 0.3.x and has been left for reference. For help with the latest Grunt 1.x release please see my comment below this question.
I'm currently trying to use Grunt.js to setup an automatic build process for first concatenating and then minifying CSS and JavaScript files.
I have been able to successfully concatenate and minify my JavaScript files, although each time I run grunt it seems to just append to the file instead of overwriting them.
As for the minifying or even concatenating CSS, I have been unable to do this as of yet!
In terms of grunt CSS modules I have tried using consolidate-css, grunt-css & cssmin but to no avail. Could not get my head around how to use them!
My directory structure is as follows (being a typical node.js application):
app.js
grunt.js
/public/index.html
/public/css/[various css files]
/public/js/[various javascript files]
Here is what my grunt.js file currently looks like in the root folder of my application:
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg: '<json:package.json>',
concat: {
dist: {
src: 'public/js/*.js',
dest: 'public/js/concat.js'
}
},
min: {
dist: {
src: 'public/js/concat.js',
dest: 'public/js/concat.min.js'
}
},
jshint: {
options: {
curly: true,
eqeqeq: true,
immed: true,
latedef: true,
newcap: true,
noarg: true,
sub: true,
undef: true,
boss: true,
eqnull: true,
node: true
},
globals: {
exports: true,
module: false
}
},
uglify: {}
});
// Default task.
grunt.registerTask('default', 'concat min');
};
So just to summarise I need help with two questions:
How to concatenate and minify all my css files under the folder /public/css/ into one file, say main.min.css
Why does grunt.js keep on appending to the concatenated and minified javascript files concat.js and concat.min.js under /public/js/ instead of overwriting them each time the command grunt is run?
Updated 5th of July 2016 - Upgrading from Grunt 0.3.x to Grunt 0.4.x or 1.x
Grunt.js has moved to a new structure in Grunt 0.4.x (the file is now called Gruntfile.js). Please see my open source project Grunt.js Skeleton for help with setting up a build process for Grunt 1.x.
Moving from Grunt 0.4.x to Grunt 1.x should not introduce many major changes.
concat.js is being included in the concat task's source files public/js/*.js. You could have a task that removes concat.js (if the file exists) before concatenating again, pass an array to explicitly define which files you want to concatenate and their order, or change the structure of your project.
If doing the latter, you could put all your sources under ./src and your built files under ./dest
src
├── css
│ ├── 1.css
│ ├── 2.css
│ └── 3.css
└── js
├── 1.js
├── 2.js
└── 3.js
Then set up your concat task
concat: {
js: {
src: 'src/js/*.js',
dest: 'dest/js/concat.js'
},
css: {
src: 'src/css/*.css',
dest: 'dest/css/concat.css'
}
},
Your min task
min: {
js: {
src: 'dest/js/concat.js',
dest: 'dest/js/concat.min.js'
}
},
The build-in min task uses UglifyJS, so you need a replacement. I found grunt-css to be pretty good. After installing it, load it into your grunt file
grunt.loadNpmTasks('grunt-css');
And then set it up
cssmin: {
css:{
src: 'dest/css/concat.css',
dest: 'dest/css/concat.min.css'
}
}
Notice that the usage is similar to the built-in min.
Change your default task to
grunt.registerTask('default', 'concat min cssmin');
Now, running grunt will produce the results you want.
dest
├── css
│ ├── concat.css
│ └── concat.min.css
└── js
├── concat.js
└── concat.min.js
I want to mention here a very, VERY, interesting technique that is being used in huge projects like jQuery and Modernizr for concatenate things.
Both of this projects are entirely developed with requirejs modules (you can see that in their github repos) and then they use the requirejs optimizer as a very smart concatenator. The interesting thing is that, as you can see, nor jQuery neither Modernizr needs on requirejs to work, and this happen because they erase the requirejs syntatic ritual in order to get rid of requirejs in their code. So they end up with a standalone library that was developed with requirejs modules! Thanks to this they are able to perform cutsom builds of their libraries, among other advantages.
For all those interested in concatenation with the requirejs optimizer, check out this post
Also there is a small tool that abstracts all the boilerplate of the process: AlbanilJS
I agree with above answer. But here is another way of CSS compression.
You can concat your CSS by using YUI compressor:
module.exports = function(grunt) {
var exec = require('child_process').exec;
grunt.registerTask('cssmin', function() {
var cmd = 'java -jar -Xss2048k '
+ __dirname + '/../yuicompressor-2.4.7.jar --type css '
+ grunt.template.process('/css/style.css') + ' -o '
+ grunt.template.process('/css/style.min.css')
exec(cmd, function(err, stdout, stderr) {
if(err) throw err;
});
});
};
You don't need to add the concat package, you can do this via cssmin like this:
cssmin : {
options: {
keepSpecialComments: 0
},
minify : {
expand : true,
cwd : '/library/css',
src : ['*.css', '!*.min.css'],
dest : '/library/css',
ext : '.min.css'
},
combine : {
files: {
'/library/css/app.combined.min.css': ['/library/css/main.min.css', '/library/css/font-awesome.min.css']
}
}
}
And for js, use uglify like this:
uglify: {
my_target: {
files: {
'/library/js/app.combined.min.js' : ['/app.js', '/controllers/*.js']
}
}
}
I think may be more automatic, grunt task usemin take care to do all this jobs for you, only need some configuration:
https://stackoverflow.com/a/33481683/1897196