When I try to build a lib in vue3, I want to set multiple output file. Code like this:
rollupOptions {
output: [
{
file: 'bundle.js',
format: 'cjs'
},
{
file: 'bundle.min.js',
format: 'iife',
name: 'version',
}
]
}
Then I will get an error:
You must set either "output.file" for a single-file build or "output.dir" when generating multiple chunks"
So, how should I do can make this work?
vite: 2.3.7
👉 You are missing the input config in rollupOptions.
The following full vite config example will generate a index.bundle.[moduleFormat].js
(you may need to adjust file path according to your setup)* :
import { defineConfig } from "vite";
// https://vitejs.dev/config/
export default (opts: { mode: "production" | "development"; command: "build" | "serve" }) => {
return defineConfig({
build: {
target: "es2020",
commonjsOptions: {
sourceMap: false
},
rollupOptions: {
input: {
index: "./src/index.js"
},
/* single
output: {
format: "umd",
strict: false,
chunkFileNames: `[name].[hash].js`,
entryFileNames: "[name].bundle.umd.js",
dir: "dist"
},
*/
// array config example
// from rollup: export type InternalModuleFormat = 'amd' | 'cjs' | 'es' | 'iife' | 'system' | 'umd';
output: [
{
format: 'cjs',
entryFileNames: "[name].bundle.[format].js",
},
{
format: 'es',
entryFileNames: "[name].bundle.[format].js",
},
{
format: 'umd',
entryFileNames: "[name].bundle.[format].js",
},
]
}
}
});
};
💡 To understand this answer better, please read the following sentence :
If you provide an array of entry points or an object mapping names to entry points, they will be bundled to separate output chunks.
And unless the output.file option is used, generated chunk names will follow the output.entryFileNames option. When using the object form, the [name] portion of the file name will be the name of the object property while for the array form, it will be the file name of the entry point.
Note that it is possible when using the object form to put entry points into different sub-folders by adding a / to the name.
If we follow the doc we can get more precisions and we know that :
output.dir
Type: string
Set in build.rollupOptions
Is for: The directory in which all generated chunks are placed and this option is required if more than one chunk is generated. Otherwise, the file option can be used instead.
output.file
Type: string
The file to write to. Can only be used if not more than one chunk is generated.
I guess your looking for https://vitejs.dev/guide/build.html#library-mode
And see https://vitejs.dev/config/#build-lib to tweak your needs.
Related
I started using grunt in my build process. In package.json I include variables to be replaced this way:
{
"name": "myApp",
"variableToReplace":{
"var1":"var1_replacement",
"var2":"var2_replacement"
},
...
}
However, I do not know how to use this variableToReplace in Gruntfile.js so that it will automatically look up all properties then replace them with the corresponding values. Is it possible using Grunt?
'string-replace': {
dist: {
files: {
//A list of files
},
options: {
//What to put here?
}
}
}
Revised Answer (after comment)
... Is there anyway to loop through the key-value pair of variableToReplace and replace, for example, var1 with var1_replacement.
Yes, this can be achieved by utilizing a Custom Task. The custom task should perform the following:
Read the variableToReplace object from package.json.
Dynamically build the options.replacements array.
Configure/set the option.replacements array using grunt.config.set
Then finally run the task using grunt.task.run.
The following Gruntfile.js demonstrates this solution:
Gruntfile.js
module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-string-replace');
grunt.initConfig({
'string-replace': {
dist: {
files: [{
expand: true,
cwd: 'src/',
src: '**/*.css',
dest: 'dist/'
}],
options: {
replacements: [] // <-- Intentionally empty and will be dynamically
// configured via `configAndRunStringReplace`.
}
}
}
});
/**
* Helper task to dynamically configure the Array of Objects for the
* `options.replacements` property in the `dist` target of the `string-replace`
* task. Each property name of the `variableToReplace` Object (found in
* `package.json`) is set as the search string, and it's respective value
* is set as the replacement value.
*/
grunt.registerTask('configAndRunStringReplace', function () {
// 1. Read the `variableToReplace` object from `package.json`.
var replacements = grunt.file.readJSON('package.json').variableToReplace,
config = [];
// 2. Dynamically build the `options.replacements` array.
for (key in replacements) {
config.push({
pattern: new RegExp(key, 'g'),
replacement: replacements[key]
});
}
// 3. Configure the option.replacements values.
grunt.config.set('string-replace.dist.options.replacements', config);
// 4. Run the task.
grunt.task.run('string-replace:dist');
});
// Note: In the `default` Task we add the `configAndRunStringReplace`
// task to the taskList array instead of `string-replace`.
grunt.registerTask('default', ['configAndRunStringReplace']);
}
Important note regarding Regular Expressions:
The docs for grunt-string-replace states:
If the pattern is a string, only the first occurrence will be replaced...
To ensure that multiple instances of the search/find string are matched and replaced the configAndRunStringReplace custom task utilizes a Regular Expression with the global g flag.
So, any instances of the following regex special characters:
\ ^ $ * + ? . ( ) | { } [ ]
which may be used in a search word (i.e. as a key/property name in your package.json) will need to be escaped. The typical way to escape these characters in a Regex is to add a backslash \ before the character (E.g. \? or \+ etc..). However, because you're using key/property names in JSON to define your search word. You'll need to double escape any of the previously mentioned characters to ensure your JSON remains valid. For Example:
Lets say you wanted to replace question marks (?) with exclamation marks (!). Instead of defining those rules in package.json like this:
...
"variableToReplace":{
"?": "!"
},
...
Or like this:
...
"variableToReplace":{
"\?": "!"
},
...
You'll need to do this (i.e. use double escapes \\):
...
"variableToReplace":{
"\\?": "!"
},
...
Original Answer
The following contrived example shows how this can be achieved:
Gruntfile.js
module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-string-replace');
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'), // 1. Read the package.json file.
'string-replace': {
dist: {
files: {
'dist/': ['src/css/**/*.css'],
},
options: {
replacements: [{
// 2. Use grunt templates to reference the properties in package.json
pattern: '<%= pkg.variableToReplace.var1 %>',
replacement: '<%= pkg.variableToReplace.var2 %>',
}]
}
}
}
});
grunt.registerTask('default', ['string-replace']);
}
Notes
Add pkg: grunt.file.readJSON('package.json') to the grunt.initConfig() section of your Gruntfile.js. This will parse the JSON data stored in package.json.
Use grunt templates to access the properties of package.json. The standard JavaScript dot notation is used to access the property values, (e.g. pkg.variableToReplace.var1), and is wrapped in a leading <%= and trailing %>
Using the contrived Gruntfile.js configuration above with your the package.json data (as described in your question). The following would occur:
Any instances of the string var1_replacement found in any of the .css files stored in the src/css/ directory will be replaced with the string var2_replacement.
The resultant files will be saved to the dist/ directory.
I'm trying to compile my js files using closure compiler, but it's giving me this error:
ERROR - goog.getMsg() function could be used only with MSG_* property or variable
my closureCompiler options are:
closureCompiler: {
options: {
compilerFile: 'temp/compiler.jar',
compilerOpts: {
compilation_level: 'ADVANCED_OPTIMIZATIONS',
//compilation_level: 'WHITESPACE_ONLY',
language_in: 'ECMASCRIPT6',
language_out: 'ECMASCRIPT5_STRICT',
formatting: 'PRETTY_PRINT',
externs: ['src/js/compiled/react-extern.js'],
warning_level: 'verbose',
summary_detail_level: 3,
output_wrapper: '"(function(){%output%}).call(window);"',
create_source_map: 'src/js/compiled/output.js.map',
manage_closure_dependencies: true,
use_types_for_optimization: null,
debug: true
},
execOpts: {
maxBuffer: 999999 * 1024
}
},
compile: {
//src: 'src/js/debug/**/*.js',
src: [
'temp/closure-library/closure/goog/base.js',
'src/js/compiled/test.js'
],
dest: 'src/js/compiled/compiled.js'
},
},
I believe I'm missing a flag, but I don't which one to write ?
You can't include goog.getMsg() in your code.
It has to be:
var MSG_SOMETHING = goog.getMsg('something');
and use the MSG_SOMETHING instead.
Google Closure Compiler enforced that, so you could write all your variable in one file and send this one to get translated.
I am stuck writing a Gruntfile which aim is to convert a bunch of Markdown files to PDF dynamically. Giving the current folder hierarchy:
root/
|_subfolder1
| |_filename1.md
|_subfolder2
|_filename2.md
...
|_node_modules
|_subfolderN
|filenameN.md
I would like to run a Markdown to PDF task which would process the md file and ouput a PDF file with the matching filename in the same output directory.
I did create a custom task which is parsing current directory, ignoring the mode_modules folder and get the markdown file, but I don't know how to configure the md2pdf task with the good properties to reflect dynamic folder mapping.
Here's my current Gruntfile:
module.exports = function(grunt) {
// 1 - Configuration
grunt.initConfig({
md2pdf: {
}
});
// 2 - Plugins
grunt.loadNpmTasks('grunt-md2pdf');
// 3 - Task registering
grunt.registerTask('default', 'Get Subfolders', function() {
grunt.file.recurse('.', callback);
function callback(abspath, rootdir, subdir, filename) {
var filenameOutExt;
// if current occurence is a file subdir == undefined
// checking subdir to true means it's not undefined and
// the current path is a directory
if(subdir) {
// excluding node_modules folder
if (!subdir.match('node_modules')) {
// only process markdown files
if(filename.match('.md')) {
filenameOutExt = filename.split('.')[0];
// now for each markdown files, run md2pdf task
// and ouput filenameOutExt.pdf in same folder
// as the input files
}
}
}
}
});
};
I am using this plugin: https://www.npmjs.com/package/grunt-md2pdf
So my questions is how should I configure the md2pdf task to pass it the markdown files and generate matching filename pdf output in same directory.
Output should be:
root/
|_subfolder1
| |_filename1.md
|_filename1.pdf
|_subfolder2
|_filename2.md
|_filename2.pdf
...
|_node_modules
|_subfolderN
|filenameN.md
|_filenameN.pdf
Thanks a lot
According to this line, this task uses the grunt.files utilitary function. This makes things easier for us!
First, it's not usual in grunt to create a different task to find the files you need in any other task.
That is, each task should receive the files it needs to operate on. For example...
coffee:
main:
files: [
expand: true
cwd: 'assets/script'
src: ['**/*.coffee']
dest: "assets/script"
ext: '.js'
]
(Note that this config is in a Gruntfile.coffee file, hence the CoffeeScript syntax)
This type of file configuration using glob expansion is one of the most common. You can find details in the documentation.
It's pretty obvious:
In every directory (**), take everything (/*) from assets/script/ that ends with .coffee. Put it into assets/script. Rename extensions to .js.
So, your task can probably be configured like that:
md2pdf: {
main: {
files: [ {
expand: true,
src: ['**/*.md', '!node_modules/**/*'],
dest: "pdf/"
} ]
}
}
Ok figured it out !
module.exports = function(grunt) {
// 2 - Plugins
grunt.loadNpmTasks('grunt-md2pdf');
grunt.registerTask('default', 'Dynamically generate PDF from MD', function() {
grunt.file.expand("./**/*.md").forEach( function(file) {
if(!file.match('./node_modules')) {
var md2pdf = grunt.config.get('md2pdf') || {};
md2pdf[file] = {
src: file,
dest: file + '.pdf'
};
grunt.config.set('md2pdf', md2pdf);
}
});
grunt.task.run('md2pdf');
});
};
I'm working on setting up series of grunt tasks that work with RequireJS r.js compiler:
1) generates a .json file listing of all files in a directory
2) strips the ".js" from the filename (requirejs requires this)
3) use grunt.file.readJSON() to parse that file and use as a configuration option in my requirejs compilation task.
Here is the relevant code from my gruntfile.js:
module.exports = function (grunt) {
grunt.initConfig({
// create automatic list of all js code modules for requirejs to build
fileslist: {
modules: {
dest: 'content/js/auto-modules.json',
includes: ['**/*.js', '!app.js', '!libs/*'],
base: 'content/js',
itemTemplate: '\t{' +
'\n\t\t"name": "<%= File %>",' +
'\n\t\t"exclude": ["main"]' +
'\n\t}',
itemSeparator: ',\n',
listTemplate: '[' +
'\n\t<%= items %>\n' +
'\n]'
}
},
// remove .js from filenames in module list
replace: {
nodotjs: {
src: ['content/js/auto-modules.json'],
overwrite: true,
replacements: [
{ from: ".js", to: "" }
]
}
},
// do the requirejs bundling & minification
requirejs: {
compile: {
options: {
appDir: 'content/js',
baseUrl: '.',
mainConfigFile: 'content/js/app.js',
dir: 'content/js-build',
modules: grunt.file.readJSON('content/js/auto-modules.json'),
paths: {
jquery: "empty:",
modernizr: "empty:"
},
generateSourceMaps: true,
optimize: "uglify2",
preserveLicenseComments: false,
//findNestedDependencies: true,
wrapShim: true
}
}
}
});
grunt.loadNpmTasks('grunt-fileslist');
grunt.loadNpmTasks('grunt-text-replace');
grunt.loadNpmTasks('grunt-contrib-requirejs');
grunt.registerTask('default', ['fileslist','replace', 'requirejs']);
I'm running into a problem where, if the "content/js/auto-modules.json" file doesn't already exist on load of my config file, the file.readJSON() is executed immediately, before the file exists and the entire task fails and throws "Error: Unable to read file " If the file already exists, everything works beautifully.
How can I set this up so that the task configuration waits for that file to be created in the first task, and modified in the second task before it tries to load & parse the JSON in it for the third task? Or is there another way (perhaps using a different plugin) to generate a json object in one task, and then pass that object to another task?
Old post but I had a similar experience.
I was trying to load a some json config like:
conf: grunt.file.readJSON('conf.json'),
but if this file did not exist then it would fall in a heap and not do anything.
So I did the following to load it and populate defaults if it didnt exist:
grunt.registerTask('checkConf', 'ensure conf.json is present', function(){
var conf = {};
try{
conf = grunt.file.readJSON('./conf.json');
} catch (e){
conf.foo = "";
conf.bar = "";
grunt.file.write("./conf.json", JSON.stringify(conf) );
}
grunt.config.set('conf', conf);
});
You still may have some timing issues but this approach may help someone with a readJSON error.
So I've got this grunt-contrib-jade setup that is working just fine
Everything is fine when I include my data in the (exported) Gruntfile myself:
module.exports = {
website: {
options: {
data: {
pageTitle: "This is my website",
greeting: "Hello world",
},
debug: true,
pretty: true
},
files: {
'build/website/index.html': 'src/jade/template/index.jade'
}
}
};
It merges my index.jade with my data values and my index.html output is the way it should be. But when I want to load an external file to define my data it goes wrong:
options: {
data: function (dest, src) {
// Return an object of data to pass to templates
return require('src/jade/template/locals.json');
},
debug: true,
pretty: true
},
files: {
'build/website/index.html': 'src/jade/template/index.jade'
}
The require path is valid, I triple checked it. It's located in the same folder as my index.jade. However I still keep getting this error:
Running "jade:website" (jade) task
>> Jade failed to compile "src/jade/template/index.jade".
>> Error: Cannot find module './locals.json'
>> Destination not written because compiled files were empty.
I tried just about everything, but I just don't see it.
Local modules need to be prepended with './' when you require them.
data: function (dest, src) {
// Return an object of data to pass to templates
return require('./src/jade/template/locals.json');
}
will work. You're not doing anything with the function (at least yet), so this could also be
data: require('./src/jade/template/locals.json')
or even
data: grunt.file.readJSON('./src/jade/template/locals.json').