Preserve original sourcemap with Browserify - browserify

Suppose I have a module whose source code is not ECMA 5 (e.g. it's Coffescript or Typescript or whatever) and is distributed in compiled form with a source map. How can I include this source map in a Browserify bundle?
For example imagine a project with a single dependency:
index.js
node_modules
typescript_module
(main.ts)
dist
main.js
main.js.map
The "main.js.map" is not consumed by browserify. That is, the browserify bundle source map maps to "main.js" instead of deferring to the original map which describes "main.ts"
For most transforms, there is a way to input source maps generated by the prior step, but is there a way to just preserve them on the original input files, when source maps already exist?

This appears to be a bug/non-feauture of Browserify:
https://github.com/substack/node-browserify/issues/772
Answering my own question because it's very hard to track down any discussion of this issue with google and no mention of it in the Browserify docs.

My setup is the following:
tsc --project tsconfig.script.json --outDir ~temp/ts
Compiles src/script.ts to ~temp/ts/script.js and ~temp/ts/script.js.map.
browserify ~temp/ts/script.js --debug | exorcist --root ../../ ~temp/bfy/script.js.map > ~temp/bfy/script.js
Compiles ~temp/ts/script.js to ~temp/bfy/script.js and ~temp/bfy/script.js.map.
sorcery --input ~temp/bfy/script.js --output dist/script.js
Reads ~temp/bfy/script.js; finds ~temp/bfy/script.js.map + ~temp/ts/script.js.map, and finally outputs dist/script.js and dist/script.js.map.
The dist/script.js.map file does reference the original src/script.ts file.
Requires Browserify, Exorcist and Sorcery (and of course CoffeeScript or TypeScript or whatever).

If you are using a TypeScript library that you have control over (for example in a lerna monorepo), you can enable the following compilerOptions in tsconfig.json:
{
"compilerOptions": {
"sourceMap": false,
"inlineSourceMap": true,
"inlineSources": true,
// ...
}
}
browserify should now use and transform the inlined source maps.
browserify will not read source maps that reference another file, it will only use inlined source maps with inlined sources. I have written about this in the referenced issue on GitHub browserify/browserify#772.
Alternatively, if you do not have control over the source of the TypeScript library, but you would still like to see the original source in DevTools, you can use the sourceify library someone mentioned in another answer. However, I had to patch it to work and I submitted a pull request. It hasn't been merged yet (at the time of writing this). If you wish to test it yourself, you can install it directly from my branch:
npm install https://github.com/jeremija/sourceify#sources-content
Make sure to use the global transform -g [ sourceify ], because the default transform (-t) in Browserify does not modify files in node_modules.

Have a look at sourceify.
Just install it:
npm i --save-dev sourceify
... and add it as a transform to package.json:
"browserify": {
"transform": [
"sourceify"
]
}
... and it Just Works.

Try the following:
var gulp = require("gulp"),
browserify = require("browserify"),
tsify = require('tsify'),
source = require("vinyl-source-stream"),
sourcemaps = require("gulp-sourcemaps"),
buffer = require("vinyl-buffer"),
uglify = require("gulp-uglify"),
header = require("gulp-header");
var settings = {
projectName : "test"
};
gulp.task("bundle", function() {
var mainTsFilePath = "src/main.ts";
var outputFolder = "bundle/src/";
var outputFileName = settings.projectName + ".min.js";
var pkg = require("./package.json");
var banner = [
"/**",
" * <%= pkg.name %> v.<%= pkg.version %> - <%= pkg.description %>",
" * Copyright (c) 2015 <%= pkg.author %>",
" * <%= pkg.license %>",
" */", ""
].join("\n");
var bundler = browserify({
debug: true,
standalone : settings.projectName
});
// TS compiler options are in tsconfig.json file
return bundler.add(mainTsFilePath)
.plugin(tsify)
.bundle()
.pipe(source(outputFileName))
.pipe(buffer())
.pipe(sourcemaps.init({ loadMaps: true }))
.pipe(uglify())
.pipe(header(banner, { pkg : pkg } ))
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest(outputFolder));
});
Note: change the paths to match your project!

Related

How to uglify a js file upon building the vue project

I have a VUE2 project and in the public folder I created an iframe.html file that will be loaded in an iframe.
That iframe will also load a javascript.js file that I want encoded/uglified upon "npm run build" but I also want to be able to access it during dev.
How could I proceed?
Should this js file be placed inside the /src/assets/ folder and referenced from the iframe.html file? If yes, any advice?
Or should it stay in the public folder and upod the dist folder being built, encode it with something.
Any solution is welcome, thanks in advance!
Edit: Here are further details of how I use the iframe.
First, I'm referencing the .vue file in the router like so:
{
path: "/pages/:id/edit",
name: "edit",
component: () => import("../views/Edit.vue"),
},
Next, in the Edit.vue file, I add the iframe like so (note how it's referencing iframe.html that is in the public directory):
<iframe
id="iframe"
ref="iframe"
src="iframe.html"
/>
Next, in the iframe.html it's just normal html code, with this part including the javascript.js file (that actually is in the public folder as well for now)
<script src="javascript.js"></script>
You can explicitly include the .js file in your Webpack config by adding a rule for UglifyJsPlugin:
npm i -D uglifyjs-webpack-plugin
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
...
module.exports = {
optimization: {
minimizer: [
new UglifyJsPlugin({
include: /\/regex-for-file/,
minimize: true
})
]
}
...
};
In Vue.config.js, this might look like:
configureWebpack: {
plugins : [
new webpack.optimize.UglifyJsPlugin({
uglifyOptions: {
include: /\/regex-for-file/,
minimize: true
}
)}
]
}
Another option is to use uglify-es; this would allow you to get even more explicit by specifying from where to copy the file during build (assuming you might want the file located outside of src/):
npm i -D uglify-es // CopyWebpackPlugin ships w/ Vue's Webpack conf by default
const UglifyJS = require('uglify-es');
const { resolve } = require('path');
const resolveAbs = (dir) => resolve(__dirname, dir);
new CopyWebpackPlugin([
{
from: resolveAbs('../external'),
to: config.build.assetsSubDirectory
},
{
from: resolveAbs('../src/custom-build-path'),
to: config.build.assetsServerDirectory,
transform: (content, path) => UglifyJS.minify(content.toString()).code;
}
]),
To be able to access it during dev, you can include the path of the js file (relative to your Vue src directory) using the resolve.alias option in the config (so you don't need to deal with possibly ridiculous relative paths in your project). Finally, you can look into webpack's HTML plugin docs for info on importing an external index.html file if needed
I would recommend not putting it in static; by default it will not be minified and built if placed in that directory.
Update/edit: Sorry, I saw a 'uglify' and just assumed you wanted uglify js. As long as the script is in your Vue project directory (or otherwise specified in the Webpack config) the file should be minified during build. Vue has pretty smart defaults for Webpack; assuming the iframe is being referenced somewhere in the app i.e. the dependency graph it will be built.

How to add tailwindcss to KotlinJS

I am unable to add the tailwindcss library to my KotlinJS project. I tried multiple things.
I have multiple dependencies defined in my build.gradle.kts
implementation(npm("postcss", "latest"))
implementation(npm("postcss-loader", "latest"))
implementation(npm("tailwindcss", "1.8.10"))
I tried creating a tailwindcss.js in my webpack.config.d with this content
config.module.rules.push({
test: /\.css$/i,
use: [
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
[
'tailwindcss'
],
],
},
},
}
]
}
);
But that doesn't do anything. I also tried modifying this with multiple options, but I was never able to get tailwindcss to compile. I also tried disabling and enabling the KotlinJS CSS support in build.gradle.kts
I can't find any info on how to add postcss to KotlinJS project.
Thank you for any help.
A basic integration can be achieved with the node-gradle plugin.
In your build.gradle.kts:
plugins {
id("com.github.node-gradle.node") version "3.0.0-rc2"
}
Also in build.gradle.kts define a task called "tailwindcss" that calls the tailwind CLI via npx. For example:
val tailwindCss = tasks.register<com.github.gradle.node.npm.task.NpxTask>("tailwindcss") {
// Output CSS location
val generatedFile = "build/resources/main/static/css/tailwind-generated.css"
// Location of the tailwind config file
val tailwindConfig = "css/tailwind.css"
command.set("tailwind")
args.set(listOf("build", tailwindConfig, "-o", generatedFile))
dependsOn(tasks.npmInstall)
// The location of the source files which Tailwind scans when running ```purgecss```
inputs.dir("src/main/kotlin/path/to/your/presentation/files")
inputs.file(tailwindConfig)
outputs.file(generatedFile)
}
Finally, in build.gradle.kts bind the task to your processResources step, so that it runs automatically. Note you may want to refine this later, because running tailwind every time the processResources step is invoked will slow down your dev cycle.
tasks.processResources {
dependsOn(tailwindCss)
}
Now we need a minimal package.json in the root of your project. For example:
{
"name": "MyProject",
"devDependencies": {
"tailwindcss": "^1.7.0"
}
}
Finally, we configure our tailwind config in the location defined by our NpxTask, in the example ```css/tailwind.css"
#tailwind base;
#tailwind components;
#tailwind utilities;
So now after the processResource step is run, gradle will invoke the Tailwind npx task, consume your source and write the CSS to the location you specified.
The accepted answer seems to not work anymore. Also, using the Node Gradle plugin is sub-optimal (KotlinJS already maintains its own package.json and yarn installation).
I managed to get Tailwind to work with KotlinJS thanks for this repository (GitHub) with a few small updates that you can find here (GitLab).
The linked I posted is the answer, the whole repository. It is not just a part of it
If you instead want me to copy/paste the whole repository instead here you're
= Kotlin/JS + Tailwind CSS =
This is a small sample repository to show the idiomatic way of
configuring these two systems together.
== Running it ==
. Run `./gradlew run`.
. Open `http://localhost:8080/` in your browser.
. 🎉 Notice we're using Tailwind CSS classes successfully.
== How To ==
Steps taken to make this work:
=== Dependencies ===
Add the following dependencies to your JS target (`jsMain` dependencies) in your Gradle file:
[source,kotlin]
----
implementation("org.jetbrains:kotlin-extensions:1.0.1-pre.148-kotlin-1.4.21")
implementation(npm("postcss", "8.2.6"))
implementation(npm("postcss-loader", "4.2.0")) // 5.0.0 seems not to work
implementation(npm("autoprefixer", "10.2.4"))
implementation(npm("tailwindcss", "2.0.3"))
----
* `kotlin-extensions` is necessary to get the JavaScript link:https://github.com/JetBrains/kotlin-wrappers/blob/master/kotlin-extensions/src/main/kotlin/kotlinext/js/CommonJS.kt#L20[`require`] function.
** Make sure the version number matches your version of the Kotlin multiplatform plugin at the top of your Gradle file.
** Kotlin Multiplatform 1.4.30 gave me `No descriptor found for library` errors. Try 1.4.21.
** Find the latest versions link:https://bintray.com/kotlin/kotlin-js-wrappers/kotlin-extensions[here].
* `postcss` and `autoprefixer` are link:https://tailwindcss.com/docs/installation#install-tailwind-via- npm[dependencies] as mentioned in the Tailwind CSS docs.
* `postcss-loader` is required because Kotlin/JS is built on top of Webpack.
** Note that while 5.0.0 is out, using it gave me build errors. The latest 4.x seems to work.
* `tailwindcss` is obviously what we're here for.
=== Add Tailwind as a PostCSS plugin ===
Just do link:https://tailwindcss.com/docs/installation#add-tailwind-as-a-post-css-plugin[this step].
If unsure, create this file in your project root:
[source,javascript]
----
// postcss.config.js
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
}
}
----
=== Create your configuration file (optional) ===
link:https://tailwindcss.com/docs/installation#create-your-configuration-file[Official documentation].
Creating the `tailwind.config.js` file is a little tricky because simply `npx` won't work, as we haven't installed any
`node_modules`. Fortunately, Kotlin/JS has already done this for us.
Run the following:
[source,shell]
----
$ ./gradlew kotlinNpmInstall
$ ( cd build/js/ && npx tailwindcss init && mv tailwind.config.js ../../ )
----
This generates `tailwind.config.js` in the `build/js/` directory and then moves it up two directories to the project
root. Kotlin/JS generates the node modules into build/js/node_modules when the kotlinNpmInstall task runs.
This assumes your JavaScript module is `js`. If it's not, you'll need to change the `cd build/js/` part. If you're not
sure where your node_modules directory is, run find . -maxdepth 3 -name node_modules.
You should now have all your dependencies set up and config files created.
=== Create and Reference a Regular CSS File ===
_If you already have a CSS file that you're loading in your app, you can skip this step._
Create `app.css` in your `jsMain/resources/` directory. Put something obvious in there so you know
when it's loaded:
[source,css]
----
body {
background-color: red;
}
----
This file will get copied into the same folder as your transpiled JavaScript files.
In your JavaScript file (`client.kt` in this package), add:
[source,javascript]
----
kotlinext.js.require("./app.css")
----
to your main method. You can of course import the require method if you prefer.
If you run `./gradlew run`, you should be able to see a red page at `http://localhost:8080/`.
We're almost there, but we have two more steps: tell Webpack to use PostCSS and to finally inject Tailwind CSS.
=== Using PostCSS with Webpack ===
We want to "monkeypatch" the Webpack configuration that Kotlin/JS generates for us. This hook is
documented in the link:https://kotlinlang.org/docs/js-project-setup.html#webpack-bundling[webpack bundling] section. Basically, if we create .js files in webpack.config.d/, they'll be automatically
merged into build/js/packages/projectName/webpack.config.js, which exists after a build and you can go inspect.
The "problem", if you have `cssSupport.enabled = true` in your Gradle file (which you should!), is that this line
generates a webpack rule matching /\.css$/. We can't simply create another rule matching the same files...that
won't work.
So, we need to find the original rule and modify it. Create the following file relative to your project root:
[source,javascript]
----
// in webpack.config.d/postcss-loader.config.js
(() => {
const cssRule = config.module.rules.find(r => "test.css".match(r.test));
if (!cssRule) {
throw new Error("Could not resolve webpack rule matching .css files.");
}
cssRule.use.push({
loader: "postcss-loader",
options: {}
});
})();
----
We use an IIFE so that our new variable doesn't potentially interfere with other unseen variables.
Now PostCSS is working!
With PostCSS configured and the `tailwindcss` npm module in our dependencies, all that's left now
is to use it.
=== Importing Tailwind CSS ===
We're basically smooth sailing from here. Follow the link:https://tailwindcss.com/docs/installation#include-tailwind-in-your-css[Include Tailwind in your CSS] directions.
Just stick the following in your `app.css`:
[source,css]
----
#tailwind base;
#tailwind components;
#tailwind utilities;
----
If you start the server again, it should **Just Work**! It's a bit hard to tell, but if you check the devtools,
you should see the tw classes loading and massive js.js file being loaded (9.20mb!) which contains all of Tailwind CSS.
== Areas for Improvement ==
=== Modifications to app.css ===
Changes made to app.css don't get picked up unless you do a full `./gradlew clean` first, which is painful.
Adding the following line to build.gradle.kts seems to fix this:
[source,kotlin]
----
tasks.withType(KotlinWebpack::class.java).forEach { t ->
t.inputs.files(fileTree("src/jsMain/resources"))
}
----
=== Getting --continuous working ===
Even with the above fix, --continuous doesn't seem to work. 🤷
== Future Topics ==
* link:https://tailwindcss.com/docs/installation#building-for-production[Building for Production]

Error when compiling Less in Gulp: `File not found with singular glob`

I'm working on a legacy site that has some pre-set-up Gulp commands.
I want to compile some .less files into .css. The existing script is as such:
gulp.task('less', function(){
return gulp.src('./src/css/less/app.less')
.pipe(less({
paths: [ path.join(__dirname, 'less', 'includes') ]
}))
.pipe(gulp.dest('./src/css/'))
});
However, when I run this I get an error:
Error: File not found with singular glob: /src/css/less/app.less (if this was purposeful, use `allowEmpty` option)
I've checked all paths, all #import and directories and they're all ok.
I am using Gulp Local: 4.0.0, CLI: 2.3.0.
Would anyone know could be causing this?
Maybe later, but Gulp 4 has a new syntax for writing tasks.
Also, you have to fix the wrong path to the source file:
// gulpfile.js
const SRC = 'src';
const DIST = 'src';
function lessTask(cb) {
return src(SRC + '/less/app.less')
.pipe(less())
.pipe(dest(DIST + '/style.css'));
}
exports.default = series(lessTask);
# On terminal:
gulp

Bundle npm module 'cheerio' in K6 test

I am trying to create some tests using K6 framework from LoadImpact, but I am struggelig with including external NPM module following the instructions on their documentation site.
On loadImpacts documentations site they include a detailed example on just what I am after, modules that enable me to parse xml from a soap service response. But, I am unable to get this working! Now, I am a total javascript newbie, but I have been coding for many years and would really like to solve this.
The can be found here: https://docs.k6.io/docs/modules#section-npm-modules
can anyone get this working? I need to run this on servers isolated from the Internet, so I am totaly dependent on creating the packages and transfer the required files.
According to the documentation a package is created like this
-- bundle `cheerio` npm module
git clone git#github.com:cheeriojs/cheerio.git
npm install browserify index.js -s cheerio > cheerio.js
My first question: In the folder I am residing when running this command a 'cheerio.js' file is created along with a a 'cheerio' folder and a 'node_modules' folder.
the cheerio.js in my "root" directory only contains the following:
+ cheerio#0.22.0
+ index.js#0.0.3
+ browserify#16.2.3
updated 3 packages and audited 2829 packages in 2.221s
found 0 vulnerabilities
Back to LoadImpacts example on how to reference this package in a k6 javascript:
import cheerio from "./vendor/cheerio.js";
export default function()
{
const res = http.get("https://loadimpact.com/");
const $ = cheerio.load(res.body);
What file is this, and where in the structure generated by browserify can I find it? I have tried to change this to point to 'index.js' in the 'cheerio' folder or cheerio.js found in 'cheerio/lib'. I will then receive a complaint about the first line in cheerio.js which defines a "parse" variable it cannot find:
var parse = require("./parse'),
if I change this to
var parse = require("./parse.js')
it goes on to complain about missing 'htmlparser2' which I can also find in this structure, but it seems like the entire dependency structure is not working.
Can anybody give me some guidance on how to create a browserify package with dependencies for cheerio and how/what I need to copy to my k6 project to make this work like on the loadImpact site.
The k6 docs for this definitely need some clarification, which I'll later do. The vendor folder currently mentioned there isn't something special, the docs are just missing a step to copy the cheerio.js and xml2js.js files that were generated by browserify to a new vendor folder in your k6 project.
For now, I'll try to offer a simplified explanation on how to achieve the same thing in a simpler way:
Create a new empty folder and go to it in a terminal
Run npm install browserify cheerio there (ignore the npm warnings about missing package.json or description)
Run ./node_modules/.bin/browserify ./node_modules/cheerio/ -s cheerio > cheerio.js in that folder
The resulting cheerio.js file in the folder root should be the file you import from the k6 script:
import http from "k6/http";
import cheerio from "./cheerio.js";
export default function () {
const res = http.get("https://loadimpact.com/");
const $ = cheerio.load(res.body);
console.log($('head title').text())
}
That should be it for a single npm library.
And if you need to use multiple npm packages, it might be better to invest some time into bundling them in a single browserified .js file. For example, if you need both the cheerio and the xml2js libraries mentioned in the k6 docs, you can do something like this:
Create a new empty folder
Add something like the following package.json file in it:
{
"name": "k6-npm-libs-demo",
"version": "0.0.1",
"description": "just a simple demo of how to use multiple npm libs in k6",
"main": "npm-main.js",
"dependencies": {},
"devDependencies": {
"browserify": "*",
"cheerio": "*",
"xml2js": "*"
},
"scripts": {
"install": "./node_modules/.bin/browserify npm-main.js -s npmlibs > vendored-libs.js"
},
"author": "",
"license": "ISC"
}
Of course, if you need different libraries than cheerio and xml2js, you need to adjust the devDependencies options.
Add an npm-main.js file like this (again, adjusting for the libraries you want):
exports.xml2js = require('xml2js');
exports.cheerio = require('cheerio');
Open that folder in a terminal and run npm install. That should result in the creation of a vendored-libs.js file in the root of the folder, which you can use in k6 like this:
import http from "k6/http";
import { cheerio, xml2js } from "./vendored-libs.js";
export default function () {
const res = http.get("https://loadimpact.com/");
const $ = cheerio.load(res.body);
console.log($('head title').text())
var xmlString = '<?xml version="1.0" ?>' +
'<items xmlns="http://foo.com">' +
' <item>Foo</item>' +
' <item color="green">Bar</item>' +
'</items>'
xml2js.parseString(xmlString, function (err, result) {
console.log(JSON.stringify(result));
});
}

Setup Babel + Uglify + Karma using Grunt

I´m trying to setup a build workflow using the aforementioned technologies, but I´m getting the following error, which seems very generic upon running tests on karma:
TypeError: 'undefined' is not an object (evaluating 'a.Sifter=b()')
This happens even without adding any ECMSA6 specific feature. The same workflow works fine without the transpiling phase in the workflow.
What I tried was to set the babeljs after a concatenation phase and before executing a uglifying on it, like the following snippet:
var defaultTasks = [
"sass:prod", // compile scss sources
"cleanAll", // clean folders: preparing for copy
"copyAll", // copying bower files
"cssmin:customVendor", // minify and concat 'customized from vendor' css
"concat:vendorStyles", // concat vendors's css + minified 'customized from vendor' and distribute as 'css/vendor.css'
"uglify:rawVendors", // minifies unminified vendors
"concat:vendorScripts", // concat vendors's scripts and distribute as 'scripts/vendor.js'
"ngAnnotate:app", // ng-annotates app's scripts
"concat:appScripts", // concat app's (customized from vendor's + ng-annotated + customer's)
"babel",// uses babeljs to convert brandnew ES6 javascript into ES5 allowing for old browsers
"uglify:app" // minify app script and distribute as 'scripts/app.js'
];
if (!skipTest) {
defaultTasks.push("karma:target"); // run tests on minified scripts
}
The imporant definitions are shown:
babel: {
options: {
"presets": ['es2015']
},
dist: {
files: {
"<%= concat.appScripts.dest %>": "<%= concat.appScripts.dest %>"
}
}
},
uglify: {
options: {
mangle: {
except: [
"jQuery", "angular", "tableau", "LZString", "moment", "Moment", "Modernizr",
"app", "modules"
]
}
},
app: {
files: [{
src: ["<%= concat.appScripts.dest %>"],
dest: "<%= app.dist %>/scripts/app.js"
}]
}
},
I´ve tested the transpile a bit, running the default logic from babel url, and it works well, converting basic stuff.
Is there any better workflow that I could use to still run the tests against the same code that would be executed for real?
Thanks
In the end, the workflow was correct.
I just need to modify the filesets a bit in order to avoid transpiling the selectize.js file (which wasn´t really needed).
However, not sure why it was breaking
That solved to me, so I´m closing the question, but perhaps might be useful for someone else.