Hi there I have been forced to come here due to every resource out there on the topic is very poor and incomplete.
Not only on the babel site but every single post out there is not complete and informative enough.
I tried to reach out at the babel forum and no replies.
I am trying to convert my prototype libraries to Es6 and convert to the most leanest possible code. So no bloaty duplicated generated code and if possible no bloaty requirejs and whatever browserify generates.
I have tried to make a project with grunt and babel directly, configure the external-helpers plugin according to the babel documentation.
It fails to include the relevant helper code and fails to include the import module code altogether.
ie a babel config like
{
options: {
sourceMap: false,
presets: ['es2015'],
"plugins": ["external-helpers"]
},
dist: {
files: {
'build/<%= package.name %>.js': ['src/<%= package.name %>.js']
}
}
}
The main project file has an import like
import Button from './ui/buttons/Button';
The module code looks like this as if the export is placed underneath extra code is generated for that.
export default class ShareButton {}
produces an output like this
Object.defineProperty(exports, "__esModule", {
value: true
});
require('babel-core/external-helpers');
var _Button = require('./ui/buttons/Button');
var _Button2 = babelHelpers.interopRequireDefault(_Button);
No source of the module or the helper object is included.
I searched hard for how to deal with external-helpers and it suggests it has to be imported into a separate file ie something like this to generate only the helper functions needed
babel-external-helpers -l createClass > src/helpers.js
But any resource regards to this fails to go as far as to how to import that into the project.
If I use the transform-runtime plugin, it produces a massive polyfill that cannot be disabled so a bug and not so useful for what I need.
"plugins": [
["transform-runtime", { "polyfill": false, "regenerator": false }]
]
If I use browserify / babelify it makes a royal mess and duplicates code still.
A config like
{
options: {
"transform": [["babelify", {
"presets": ["es2015"],
"plugins": ["external-helpers"],
sourceMap: false
}]]
},
dist: {
files: {
'build/<%= package.name %>.js': ['src/<%= package.name %>.js']
}
}
}
Produces code like this still with the external helper missing and duplicated code not relevant to the helper. ie
Object.defineProperty(exports, "__esModule", {
value: true
});
Is within every module in the generated file.
If I export the classes like this at the bottom of every file
export default class {}
Duplicated code is generated like
var _class = function _class() {
babelHelpers.classCallCheck(this, _class);
};
exports.default = _class;
In terms of filesize that doesn't include bloaty wrapping code like
},{}],2:[function(require,module,exports){
It seems concatting all the prototype classes files together to bundle in one file is the winner still.
So trying to port the library but keep it similar and bundle it together into one file.
Hopefully this is concise enough and there is a simple solution.
FYI browsers do not understand tabs and 4 spaces. I had to edit this post in my editor to get the code blocks working ! It would be nice to have a markup like other places like "```" ?
Let me know thanks.
I'm using rollup with babel now. Rollup produces a clean output as umd mode. Browserify is really bloaty in itself.
There is just a problem with polyfills being converted. I have to concat external ones in like for WeakMap.
I had a problem trying to use the generated Iterator and finding a polyfill for that so I have to code loops a particular way it doesn't generate Iterators.
The polyfill generation in babel is still too bloaty and crazy. It's pretty terrible. So I concat in minified polyfills that are very small and it's used globally.
I was running into something very similar. Was tired of trying to do it the "right way" and ended up just creating https://www.npmjs.com/package/grunt-babel-helpers which simply manipulates the string output.
Related
New to pnpm, and trying to get my head around some of the basics. But can't find a lot of documentation around it (which often means that it's either very simple, or I'm doing it wrong...).
I have set up a basic pnpm monorepo with an apps and packages folder by basically creating the monorepo folder, running pnpm init and tweaking the result a bit. I got:
package.json
{
"name": "#myorg/root",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
pnpm-workspace.yaml
packages:
- "packages/**"
- "apps/**"
.npmrc
shamefully-hoist=true
Notes:
I did not create an index.js, as I have no idea what to put in there..
I know that I can run/build the various apps from the root folder by adding pnpm run dev with a filter to the scripts section, I've not yet gotten around to setting that up, but I believe it's not critical to running the monorepo. (?)
In the apps folder I've already created some Vue3 apps (this works fine). And now I'd like to move some of the Vue components used there into the packages folder of the monorepo, so I can reuse them in the various apps. This is where i'm getting stuck in the sand...
I'm not entirely sure how much scaffolding you're supposed to add to these shared components. Is each one an entire Vue-project by themselves? (I'm guessing yes), and then, how to specify in that project what parts to export?
I have created the folder "y-theme-select" in the "packages" folder, and ran pnpm init and pnpm add vue on it. Now lets say I want to add the following component (let's keep it very simple):
y-theme-select.vue
<template>
<div>
Hello world!
</div>
</template>
Where do I store it? (eg. packages\y-theme-select\src\y-theme-select.vue?)
How do I export it? (clueless)
How do I import it in a shared project (I recon something like "#myorg/y-theme-select": "1.0.0" in the dependencies section of the package.json?)
How to use Vuetify components in here?
Nb. for completeness sake, found two related questions:
Multiple Vue apps, shared components in a monorepo (unanswered, and doesn't specify pnpm monorepo)
How to create my own component library based on Vuetify
First off, the reason it's not documented at pnpm is that it's, except for a few properties, not a PNPM concern.
Secondly, what I found is that reusable components all share a few basic principles, but other than that can vary fairly wildly in setup.
Thirdly, this answer works. But has a few issues, as described at the end of the answer.
I also want to mention the excellent video by "How To Create A Vue.js Plugin Component" by Erik Hanchett, which laid a foundation to this answer.
UPDATE: I stopped building components. As you add functionality to them there's always some new weird issue. Now that scoped CSS turned out to not work, I've changed direction. Here is a super-simple low-tech solution to creating a library of components in this pnpm monorepo:
import YSwitchLang from "/../../packages/common-vtfy/src/components/YSwitchLang.vue"
Just reference the packages folder from within your apps project. (Fingers crossed I won't run into anything new, but so-far so-good.) The instructions below are still valid, but in this scenario you only need step I.
I. Initialization of the project
I'm creating a package that will hold a few generic and similar Vuetify components, so I will call it "common-vtfy". This project will use Vite+Rollup as bundlers. I also use the rollup-plugin-typescript2 package to create the typescript definitions. You can simply leave out vuetify package if your component doesn't depend on it.
cd packages
pnpm create vue#latest
-> common-vtfy
-> Typescript
-> ESLint
cd common-vtfy
echo auto-install-peers = true > .npmrc
pnpm add -D vuetify rollup-plugin-typescript2
pnpm install
In package.json:
prefix the package name with your monorepo name, like so: "name": "#myorg/common-vtfy".
Move "vue": "^3.2.45" entry to devDependencies (not a biggie to leave it here, because we externalize it as well in the build section of vite.config.ts)
Add peerDependencies (not sure if this is needed, but probably won't hurt):
"peerDependencies": {
"vue": "~3.x",
"vuetify": "~3.x"
}
At this point you could run pnpm dev, and see an otherwise empty Vue project, which has way more stuff than we'll be requiring, so go ahead and delete:
public\favicon.ico
src\assets\logo.*
src\components*
Not sure if we could/should delete the css files from src\assets as well. #TBD.
II. Build component
Now we create the components, and setup App.vue to see the results:
YSwitchTheme.vue
<script setup lang="ts"></script>
<template>
<div>
Hello, I'm YSwitchTheme <v-chip>Vuetify Test</v-chip>
</div>
</template>
And similarly for YSwitchLang.vue
App.vue
<script setup lang="ts">
import YSwitchTheme from "./components/YSwitchTheme.vue" // not required if using the vue plugin system
</script>
<template>
<div>
<YSwitchTheme/>
</div>
</template>
III. Create the plugin
Create two files:
src\components\index.ts
export {default as YSwitchLang} from "./YSwitchLang.vue"
export {default as YSwitchTheme} from "./YSwitchTheme.vue"
I believe that this "registers" the components, but the details are not exactly clear to me.
src\CommonVtfyPlugin.ts
The plugin entry file. More information: https://vuejs.org/guide/reusability/plugins.html#writing-a-plugin
I have tried to export the components both as a plugin, and as a individually importable components, which does not require the user to load it as a plugin. However, this did not end up working, so I've commented out that last bit. The plugin must be imported using the Vue plugin system (more on that later)
import type { App } from "vue"
import { YSwitchLang, YSwitchTheme } from "./components"
// Export as plugin
export default {
install: (app: App) => {
app.component("YSwitchLang", YSwitchLang)
app.component("YSwitchTheme", YSwitchTheme)
}
}
// Export as individually importable components
// export { YSwitchLang, YSwitchTheme }
IV. "Local" testing/usage demonstration
To use the plugin we add it to our main.ts, and this is something we can do in this same project. The resulting code is the same as you would use when you are importing it later in your other projects.
main.ts
Add import:
import CommonVtfyPlugin from './CommonVtfyPlugin'
If you're using Vuetify, then also add:
// Vuetify
import 'vuetify/styles'
import { createVuetify } from 'vuetify'
import * as components from 'vuetify/components'
import * as directives from 'vuetify/directives'
const vuetify = createVuetify({
components,
directives,
})
And add the .use clause, in the following manner:
const app = createApp(App)
app.use(vuetify).use(CommonVtfyPlugin)
app.mount('#app')
Now in App.vue, comment out the import statements.
V. Build it
Here we're going to use rollup-plugin-typescript2 to generate the typescript files.
vite.config.ts
Add to the imports:
import vuetify from "vite-plugin-vuetify"
import typeScript2 from "rollup-plugin-typescript2"
Add to the plugins:
vuetify({
autoImport: true,
}),
typeScript2({
check: false,
include: ["src/components/*.vue"],
tsconfigOverride: {
compilerOptions: {
sourceMap: true,
declaration: true,
declarationMap: true,
}
},
exclude: [
"vite.config.ts"
]
})
Add a new section build to the defineConfig:
build: {
cssCodeSplit: false,
lib: {
entry: "./src/CommonVtfyPlugin.ts",
formats: ["es", "cjs"],
name: "CommonVtfyPlugin",
fileName: format => (format == "es" ? "index.js" : "index.cjs"),
},
rollupOptions: {
external: ["vue"],
output: {
globals: {
vue: "Vue"
}
}
}
},
Now you're ready to build it by running pnpm build.
Wrap it up by updating package.json, adding four properties:
"type": "module",
"exports": {
".": "./dist/index.js"
},
"types": "./dist/index.d.ts",
"files": [
"dist"
],
One issue the I've not yet figured out is how to generate a single index.d.ts declarations file. For now I just create the following file by hand in the dist folder. Inspired by the Vuetify project, I have not yet figured out why/how this works.
index.d.ts
declare module '#myorg/common-vtfy' {
import { VueConstructor } from 'vue'
const YSWitchLang: VueConstructor
const YSWitchTheme: VueConstructor
export {
YSWitchLang,
YSWitchTheme
}
}
VI. Use it!
Go back to a project that wants to use these components and add them to the project using pnpm add #myorg/common-vtfy (replace myorg with the name of your monorepo). You should see a new dependency in the package.json file that reads something like "#myorg/common-vtfy": "workspace:^1.0.0".
main.ts or plugins\index.ts (wherever you load your plugins)
Import the components:
import YSwitchTheme from '#myorg/common-vtfy'
import YSwitchLang from '#myorg/common-vtfy'
I was expecting to be able to import the modules using a {}-style import, but this doesn't work. I think this means that we're not correctly exporting the components from the plugin. See issues section.
import { YSwitchTheme, YSwitchLang} from '#myorg/common-vtfy'
And finally, to use the plugin, do:
app.use(YSwitchTheme)
app.use(YSwitchLang)
VII. Updating
At some point you're going to make changes to the component.
Component: Make the changes
Component: pnpm build
Component: Recreate index.d.ts
Application: pnpm update #myorg/common-vtfy
Open issues
Scoped CSS is ignored. I've tried all kinds of different rollup settings.
Confirm that peerDepencies is either a good thing or useless.
I've not been able to figure out how to generate a single index.d.ts typescript declaration file.
Pnpm monorepo update
I think you should run the pnpm update on the whole repo. But haven't dived into that yet.
I will attempt to update and further refine this answer as I gain understanding, and/or when usefull comments are posted here.
I have a web application built with webpack. I have many styling variations, and those styles are all called style.css and in their own respective directories like
./STYLE_A/style.scss
./STYLE_B/style.scss
./STYLE_F/style.scss
I am supplying a cross-env variable STYLE_DIR to webpack and I want that variable to control where the scss gets included from.
I've tried:
require(`./${STYLE_DIR}/style.scss`); //in the webpack (does nothing)
I've tried:
require(`./${STYLE_DIR}/style.scss`); //in my client.js (ends up including every style.scss from every one of the style directories)
I've tried setting this to a 'process.env' variable in webpack, I've tried using an alias to resolve, there's something I'm just missing.
I got interested in your question, then I did a little research and I think I have a way to make it work.
Steps:
1.
In Webpack config file use DefinePlugin in order to have a constant that can be setup at compile time. You do that in this way:
const GLOBALS = {
'process.env.STYLE_DIR': JSON.stringify(process.env.STYLE_DIR)
};
export default {
entry: [
'./app/index'
],
output: {
path: path.resolve(__dirname, '/dist'),
filename: 'bundle.js'
},
plugins: [
new webpack.DefinePlugin(GLOBALS)
],
...
}
2.
Put your style.scss file in the correct folders (STYLE_A, STYLE_B and STYLE_C as you indicated).
3.
In your .js file require your SCSS file as follow (of course be sure to have the corresponding loaders properly setup in Webpack config file):
require(`./${process.env.STYLE_DIR}/style.scss`);
4.
Set the STYLE_DIR variable before you run webpack. Something like this:
export STYLE_DIR = 'STYLE_A'
This is working for me. If I change the STYLE_DIR value before running Webpack I get a different style file imported.
I hope this helps.
I'm using JSHint (in grunt tasks) on a project and currently have it setup to use "jshint-stylish" as the reporter. It's defined in the jshint.js file as seen in the following code
module.exports = {
options: {
reporter: require('jshint-stylish'),
but I'd like to use my own custom reporter. How do I define the reporter to identify the custom reporter?
Using the structure identified in https://stackoverflow.com/a/17493367/5144741, I figured out that my relative path was incorrect. Important to note: don't include the extension when identifying the reporter. And the require statement isn't needed in this case either.
module.exports = {
options: {
reporter: './example/folder/src/reporters/my_reporter',
So in my config file, I'm statically defining the css files to watch, along with some html partials. (eventually I will minimatch with exclusions .. I'm just going with first pass right now)
Originally I was storing these in the grunt config object, but struggled to get the output I wanted, so I moved them out of the initConfig method and into the wrapping function:
Original pass:
grunt.initConfig({
cssFiles: [ ... list of files ... ],
htmlFiles: [ ... list of files ...],
watch: {
reload: {
files: ['<%= cssFiles.concat(htmlFiles).join(",") %>']
}
}
});
I tried several variations of this (with and without join), as an example.
Current "Working" version:
module.exports = function(grunt) {
var cssFiles = ['someFile.css',...'lastFile.css'],
htmlFiles = [ ... ];
grunt.initConfig({
watch: {
reload: {
files: cssFiles.concat(cshtmlFiles)
}
}
});
};
I feel like I should be able to do this without having to move my array's out of the grunt config (although I don't know WHY I feel they should stay there ... I guess I just haven't seen many Gruntfile's with code outside of initconfig)
I'm using a system that stores all the paths I need in a single config object, like so:
grunt.initConfig({
pathTo: {
distcss : './dist/css/master.css',
srcstyles : './lib/styles/**/*.scss',
vendor : './lib/vendor'
},
// tasks...
});
Then, I load those in via underscore templates like in your first example. If your project is structured in a good way then usually just having one minimatch pattern is enough. All my CSS ends up in lib/styles, and any misc. third party stuff is usually in lib/vendor as that is managed through Bower.
With the right directory structure and pattern you shouldn't need a large array of paths. A sample JavaScript project could look like this:
lib
└── src
├── app
└── tests
Then your minimatch pattern to lint your application and test code would just be lib/src/**/*.js, for example.
But what works for you works for you; if you've written a system that you're happy with, regardless of whether you've seen it elsewhere or not, there's no reason to change it. :-)
I'm trying to use grunt-contrib-livereload, but can't seem to figure it out. The readme seems to skip over everything that I need explained, and then ends with an example that doesn't work when I try it and doesn't seem directly related to the documentation. I have searched for a better explanation in a blog post or tutorial or whatever, but haven't been able to find one. Can someone please explain how to get started with this tool?
Here are the kinds of questions I have, based on the readme:
The documentation says the livereload task "must be passed the list of files that have changed" --- but how do I pass it this list of files? The example does not seem to illustrate this. Does regarde pass the list?
Is grunt-contrib-connect required? What does it do, and how do I use it? Do I need to learn connect before I try using livereload?
The readme mentions middleware that "must be the first one inserted" --- but inserted into what, before what else? And how is it inserted?
And I suppose I don't understand how I need to manipulate ports. "All the browsers listening on the livereload port will be reloaded" --- but how do I know which browser is listening to which port? Do I need to learn all about ports before I can try using livereload? (Any suggestion on how to best learn about that?)
Finally, in the example, there is a folderMount function that doesn't seem related to any of the documentation before. What is that, and do I need it?
I guess I'm asking if someone can please:
point me towards a tutorial that is much more effective than the current readme;
explain these unexplained parts of the readme, if those answers are what I need to understand the plugin;
or provide a functional example with some explanation of why it is functional.
Live reloading is now built into grunt-contrib-watch version 0.4.0. grunt-contrib-livereload and grunt-regarde will be deprecated soon.
Now just set the option livereload to true in your config and it will create a live reload server then reload after the tasks have run:
grunt.initConfig({
watch: {
all: {
options: { livereload: true },
files: ['lib/*.js'],
tasks: ['jshint'],
},
},
});
By default the live reload server will be started on port 35729. So to enable live reloading on your page just add <script src="http://localhost:35729/livereload.js"></script> to your page.
View more info on the docs: https://github.com/gruntjs/grunt-contrib-watch#live-reloading
Edit: Check versioning info. grunt-contrib-watch now has livereload support baked in.
What a doozy. I ran into issues with this one too so let me do what I can to explain (or at least get you up and running). Keep in mind, this is how I have it set up and it seems to work most of the time.
For starters, you'll want to make sure you've udpated your package.json with the right dependencies. I'm not sure that livereload works with the baked in "watch" task and I've been using grunt-regarde of late. My package.json usually looks like this:
"dependencies": {
"grunt": "~0.4.x",
"grunt-contrib-livereload": "0.1.2",
"grunt-contrib-connect": "0.2.0",
"grunt-regarde": "0.1.1"
},
Obvi you want grunt (duhhh), livereload, connect seems to help with mounting folders, and regarde is like grunt-watch, it just seems to work better (I forget why exactly).
You could make your package.json even better by specifying livereload in its own "devDependencies" object if you're so inclined. Now, run your good old fasioned npm install to get the goodies in your project.
Let's talk gruntfiles:
As you probably know, the gruntfile is what makes the magic happen. Somewhere towards the bottom of your gruntfile, you'll want to specify
grunt.loadNpmTasks('grunt-regarde');
grunt.loadNpmTasks('grunt-contrib-livereload');
grunt.loadNpmTasks('grunt-contrib-connect');
At the top of your gruntfile, we'll want to add some utils for livereload. Under /*global module:false*/, go ahead and add var lrSnippet = require('grunt-contrib-livereload/lib/utils').livereloadSnippet;.
After that, you don't really need to learn connect, you just gotta use it. Check my style:
var folderMount = function folderMount(connect, point) {
return connect.static(path.resolve(point));
};
This comes before module.exports = function(grunt) {
Now let's get into the meat of the gruntfile. Again, I forget what connect is doing but this is where the middleware magic comes into play. In your modules.exports, add:
connect: {
livereload: {
options: {
port: 9999,
middleware: function(connect, options) {
return [lrSnippet, folderMount(connect, '.')]
}
}
}
},
Now we want to have the files watched. I like to set up a few different tasks since I don't want my whole grunt process running every time I save a CSS file. Here's what I work with (again, add to module.exports):
regarde: {
txt: {
files: ['styles/*.css', 'index.html'],
tasks: ['livereload']
},
styles: {
files: ['sass/*.scss', 'sass/*/*.scss'],
tasks: ['compass']
},
templates: {
files: ['templates/*.jade'],
tasks: ['jade']
}
},
You can see that I only want livereload to fire when there have been changes to my compiled css (*.css) or to my compiled html. If I edit a SCSS file, I want to fire off just compass. If I edit a jade template, I want to only fire the jade to HTML compiler. I think you can see what's going on. You can toy with this, just be smart about it because you could get caught in an infinite loop.
Lastly, you need to fire off these processes. I like tying them all to my main grunt task because my gruntfile is just that sweet.
// Default task.
grunt.registerTask('default', ['livereload-start', 'connect', 'regarde']);
Now, when you fire up grunt in the CLI, you should (hopefully, maybe, cross your fingers) get something like this:
Running "connect:livereload" (connect) task
Starting connect web server on localhost:9999.
Browse to http://localhost:9999/yourpage.html and watch magic happen.
full gruntfile here. full package.json here.
Here is a solution based on Gulp instead of Grunt and the following Gulpfile.js to get livereload working:
var gulp = require('gulp');
var connect = require('connect');
var connectLivereload = require('connect-livereload');
var opn = require('opn');
var gulpLivereload = require('gulp-livereload');
var config = {
rootDir: __dirname,
servingPort: 8080,
// the files you want to watch for changes for live reload
filesToWatch: ['*.{html,css,js}', '!Gulpfile.js']
}
// The default task - called when you run `gulp` from CLI
gulp.task('default', ['watch', 'serve']);
gulp.task('watch', ['connect'], function () {
gulpLivereload.listen();
gulp.watch(config.filesToWatch, function(file) {
gulp.src(file.path)
.pipe(gulpLivereload());
});
});
gulp.task('serve', ['connect'], function () {
return opn('http://localhost:' + config.servingPort);
});
gulp.task('connect', function(){
return connect()
.use(connectLivereload())
.use(connect.static(config.rootDir))
.listen(config.servingPort);
});
I know this is a little old but can help someone.
In the Gruntfile.js add "options":
sass: {
files: 'scss/**/*.scss',
tasks: ['sass'],
options: {
livereload: true,
}
}
In the index add:
<script src="http://localhost:35729/livereload.js"></script>