Rollup get list of inputs (like esbuild metafile) - javascript

Yo!
I have to use Rollup's JavaScript API to bundle some code directly in the browser (it's an online REPL).
I use UNPKG to fetch the packages, and at some point, I need a list of all the imported file, packages etc.
I am used to bundle in the browser using esbuild; with esbuild's JavaScript API I can get the list I need, once the bundle is created, by accessing bundle?.metafile?.inputs.
The bundle.metafile.inputs looks like this :
{
'a:App.jsx': {
bytes: 368,
imports: [
{
kind: "import-statement",
path: "b:https://unpkg.com/react"
},
{
kind: "import-statement",
path: "b:https://unpkg.com/react-dom/client"
}
]
},
'b:https://unpkg.com/react': {
bytes: 190,
imports:[
{
kind: "require-call",
path: "b:https://unpkg.com/react#18.2.0/cjs/react.production.min.js"
}
]
}
// ... here all of the other imports and imports of imports
}
I looked for something similar in Rollup's documentation but couldn't find any.
Is there anything similar to what I need in Rollup's JS API, or perhaps any existing plugin specific for a usecase without file-system (I mean, with just a virtual one), or do I have to create a similar plugin myself?
Thx in advance,

Thx to this CodeSandbox,I found out that the result of rollup bundle in the browser is an object structured like so:
{
"output":[
{
"code":"'use strict';\n\nObject.defineProperty(exports, '__esModule', { value: true });\n\nvar lodash = require('lodash');\n\nconst test = lodash.camelSize('yo World');\nconst a = (c, d) => {\n return [c + d, test];\n};\n\nexports.a = a;\n",
"dynamicImports":[
],
"exports":[
"a"
],
"facadeModuleId":"/src/index.a.js",
"fileName":"index.a.js",
"imports":[
"lodash"
],
"isDynamicEntry":false,
"isEntry":true,
"map":null,
"modules":{
"/src/index.a.js":{
"originalLength":126,
"removedExports":[
],
"renderedExports":[
"a"
],
"renderedLength":91
}
},
"name":"index.a",
"type":"chunk"
}
]
}
Each object in the output array, includes a property called imports, which is, as the name suggests, an array listing all of the imports in that bundle.

Related

iterating over data including image in Gatsby

In Gatsby I would like to iterate over an array, which contains objects. One of the properties of each object would be an image. I would like to be able to use Gatsby Image.
Here is one example of when I'd like to do so: a page on a website with a gallery of images, each image opens a particular associated video when clicked. Perhaps I'd like 20, 50, or even 100+ objects in the array:
const videos = [
{
id: 1,
name: 'Festival 2018',
url: 'https://www.youtube.com',
img: // HOW TO ACHIEVE?
},
// Many more objects
]
videos.map((item) => {
return (
<Img
key={item.id}
fluid= // HOW TO ACHIEVE?
alt={item.name}
onClick={() => openPlayer(item.url)}
/>
)
})
I understand how to query for single images with GraphQL; or how to query multiple images and use aliases; or how to query all images from a folder. But I have't worked out how to achieve my goal. There's probably a better way. Thanks in advance.
To use internal images in gatsby-image you need to allow Gatsby and their transformers and sharps to know where the images are located using the gatsby-source-filesystem. This will create queryable nodes from your images and will allow you to use gatsby-image with them.
Applied to your case, you need to put all images in the same folder, let's say /src/images and:
const path = require(`path`)
module.exports = {
plugins: [
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: path.join(__dirname, `src`, `images`),
},
},
`gatsby-plugin-sharp`,
`gatsby-transformer-sharp`,
],
}
There, your JSON object will look like:
const videos = [
{
id: 1,
name: 'Festival 2018',
url: 'https://www.youtube.com',
img: '/relative/path/to/your/image.extension',
extension: 'png'
},
// Many more objects
]
Note: thanks John for the clarification about the extension field. Source: https://stackoverflow.com/a/56012718/13714522
In your case, since you are using a JSON-based source, you will need to add the gatsby-transformer-plugin. The configuration will look like:
module.exports = {
plugins: [
`gatsby-transformer-json`,
{
resolve: `gatsby-source-filesystem`,
options: {
path: `./src/data/`,
},
},
],
}
Note: assuming that the JSON is placed in /src/data
Also assuming that your JSON file is named data.json, if everything is properly set, Gatsby will create a GraphQL node called allDataJson. Then you only last to create a query (page query or static query) with the following content:
{
allDataJson {
edges {
node {
name
url
id
img {
childImageSharp {
fluid(maxWidth: 1000, quality: 100) {
...GatsbyImageSharpFluid
}
}
}
}
}
}
}
Note: check your exact query in the localhost:8000/___graphql playground
If your paths are correct, Gatsby will create the childImageSharp node which will allow you to use your own internal images within gatsby-image feature. Since your queried data is stored inside props.data, your final loop should look like:
props.data.allDataJson.edges.node.map((item) => {
return (
<Img
key={item.id}
fluid={item.img.childImageSharp.fluid}
alt={item.name}
onClick={() => openPlayer(item.url)}
/>
)
})

No matching client found for package name '', but google-services.json is present in the app directory

I'm getting a little desperate here. My React-Native app has two app folders and both these folders contain a google-services.json file with the correct structure and a "package_name" is definitely present and has the correct value. The file looks like:
{
"project_info": {
"project_number": "123456789123",
"firebase_url": "https://myproject.firebaseio.com",
"project_id": "my-project",
"storage_bucket": "my-project.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:878210515221:android:64cb9c9ba170c841",
"android_client_info": {
"package_name": "com.mycompany.appstore.myproject"
}
},
"oauth_client": [
{
"client_id": "123456789123-ipr3gfqml3oaajpk1lsq6gvf81mebkcm.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "mykey-xxxx-xxxx-xxxx"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 1,
"other_platform_oauth_client": []
},
"ads_service": {
"status": 2
}
}
}
],
"configuration_version": "1"
}
Both google-services.json files have the same content (I've replaced some values with dummy text in this example), but the react-native run-android command keeps giving me the No matching client found for package name '' warning, and it doesn't even mention a package_name but says it's an empty string.
All solutions on the internet are "Put a google-services.json" file in your app folder. That clearly doesn't help me in this case. Any idea what the issue is?
if you have multiple Flavor you need to create multiple client on firebase portal for same application
for example (in android)
productFlavors {
prod {
applicationId "com.xyz.abc"
dimension "tier"
}
apiary {
applicationId "com.xyz.abc.apiary"
dimension "tier"
}
local {
applicationId "com.xyz.abc.local"
dimension "tier"
}
}
otherwise check your package name in app and google-services.json

React Native - Dynamic Image Source

I'm trying to loop through the SOURCE array with the map method, but I keep getting this error:
Unknown named module: '../images/one.jpeg'
Anyone know why this is happening? The file path in the require is definitely correct.
var SECTIONS = [
{
title: 'One',
fileName: 'one.jpeg',
},
{
title: 'Two',
fileName: 'two.jpeg',
},
{
title: 'Three',
fileName: 'three.jpeg',
},
{
title: 'Four',
fileName: 'four.jpeg',
},
];
{SECTIONS.map((section, i) => (
<CategoryCard
key={i}
source={require(`../images/${section.fileName}`)}
title={section.title}
/>
))}
I don't think this is possible because react native needs to know what to bundle ahead of time (AFAIK). However, you can require all the files in your array:
var SECTIONS = [
{
title: 'One',
file: require('../images/one.jpeg'),
},
{
title: 'Two',
file: require('../images/two.jpeg'),
},
{
title: 'Three',
file: require('../images/three.jpeg'),
},
{
title: 'Four',
file: require('../images/four.jpeg'),
},
];
{SECTIONS.map((section, i) => (
<CategoryCard
key={i}
source={section.file}
title={section.title}
/>
))}
You can't use dynamic links. The best hack that i found to solve this is this:
var SECTIONS = {
One: {
title: 'One',
file: require('../images/one.jpeg'),
},
Two: {
title: 'Two',
file: require('../images/two.jpeg'),
},
Three: {
title: 'Three',
file: require('../images/three.jpeg'),
},
Four: {
title: 'Four',
file: require('../images/four.jpeg'),
},
};
{SECTIONS.map((section, i) => (
<CategoryCard
key={i}
source={section.file}
title={section.title}
/>
))}
That way, you can just use the files and if you have some kind of dynamic image selection, you can just use something like this
<Image source={SECTIONS[image.type]} />
try opening the file in separate browser using direct URL something like
http://<><>/imgages/one.jpg
You can also do something like this as well:
One working example for displaying dynamic images using react :
Example Click Here
Got a working solution, though not recommended for large images, works perfectly for (a lot of)small images.
Steps:
Convert the icon(s) to base64 string(s).
Create a JSON file with filename as the keys and the base64 strings as values.
(You can also store them to a local database)
e.g.
ImageData.json
{
"icon1": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQ.......==",
"icon2": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQ.......=="
}
3.Import the json file to the place where you require the images dynamically.
e.g.
const imageData = require("./images/ImageData.json")
4: Get/generate the key/filename at runtime. and get the image source.
e.g.
const imageSrc = imageData[keyname]
5: Generate a image dynamically at runtime.
e.g.
<Image style={{ width: 70, height: 70, resizeMode: Image.resizeMode.contain }} source={ uri: imageSrc } />
Done..
Extra..
Written a helper python script to automate the json file creation.
import base64
import os
directory = os.fsencode('.')
with open('ImagesData.json', 'wb') as jsonFile:
jsonFile.write(bytes('{', 'utf-8'))
written = False
for file in os.listdir(directory):
filename = os.fsdecode(file)
if filename.endswith('.png'):
with open(filename, "rb") as image_file:
if written:
jsonFile.write(bytes(',\n','utf-8'))
encoded_string = base64.b64encode(image_file.read())
jsonFile.write(bytes(('"' +filename+ '":'), 'utf-8'))
jsonFile.write(bytes('"data:image/png;base64,', 'utf-8') + encoded_string + bytes('"', 'utf-8'))
written = True
jsonFile.write(bytes('}', 'utf-8'))
Copy the script to the image folder and run the script (requires python 3.6).
A json file will the created with image name as key and base64 string as values.
Copy the file to project and use (You can delete the images after that).
Use the json file as mentioned above.
I had the same problem but my situation was a little different. I had an array of different objects that needed dynamic images. I was already mapping the array, but I needed to match the images to that array based off of name. It was a little hacky, but this is how I went about it.
First, in my parent component I created a function to render a component for my array of objects. I passed the objects data into a prop called "object".
In my case I knew what my data was and I needed to match the corresponding image to the object that was being pulled off of an external api that I was grabbing my data from.
renderObjects() {
return this.state.objects.map(object => (
<ObjectListDetail
key={object.id}
next
props={this.props}
object={object}
/>
));
}
In my ObjectListDetail component, I created a variable called icons, which was another array of objects. This time, I created a name property that would match the object being passed to the component from the parent and then had a second key called source in which I provided the path to the image. It went something like this.
var icons = [
{ name: "BTC", source: Images.btc },
{ name: "ETH", source: Images.eth },
{ name: "ETC", source: Images.etc },
{ name: "ZRX", source: Images.zrx },
{ name: "USDC", source: Images.usdc },
{ name: "LTC", source: Images.ltc },
{ name: "BCH", source: Images.bch },
{ name: "USD", source: Images.usd }
];
NOTE *** I had already required all of my images into a separate file for my entire app and imported them at the top.
I then created a variable called imgSrc and filtered the result to match the name of the object i was passing to the child component.
var imgSrc = icons.filter(
icon => icon.name === props.wallet.name
)
I then created an Image component and in the source requirement I called the result of the filter and pointed it to the source.
<Image source={imgSrc[0].source} />
That is how I achieved dynamic image rendering within my application.
Its probably not the best way to do things, and I am still kinda new at this, but I would love any criticism

create custom tags with jsdoc

I am trying to create custom tags in jsdoc 3.4.2. The config.json file is
{
"tags": {
"allowUnknownTags": true,
"dictionaries": ["jsdoc","closure"]
},
"source": {
"include": [
"app/"
],
"exclude": [],
"includePattern": ".+\\.js(doc|x)?$",
"excludePattern": "(^|\\/|\\\\)_"
},
"plugins": [
"plugins/custom-tags.js"
],
"templates": {
"cleverLinks": false,
"monospaceLinks": false
},
"opts": {
"destination": "./docs",
"recurse": true,
"encoding": "utf8"
}
}
In the custom-tags.js i have added these lines
exports.defineTags = function (dictionary) {
dictionary.defineTag("service", {
mustHaveValue: true,
canHaveType: false,
canHaveName: true,
onTagged: function (doclet, tag) {
doclet.service = tag.value;
}
});
};
But when i used the #service in the code, it is not showing. I had looked some link relating this and found out for custom tags we need to create template, but not found a way of creating one. I had installed jsdoc globally on my windows machine.
You are correct there is a two step process.
First you define a tag for jsdoc to find in the code and update its doclet object (like you have done)
Second you need the template, the thing which turns the doclet object into HTML, to know about the new property and do something with it.
Like you I've had a hard time finding instructions on making templates. The best I can suggest is check the jsdoc source code. You'll need to create a JavaScript file which exposes a publish function. The publish function will then iterate over the doclet object to generate HTML.
I had the same need as you but all I wanted to do was add a new section (header and text maybe a table of parameters) to the existing jsdoc template. I didn't really want to go and create a whole new template just for that so I ended up defining my tags in a way that they would end up appending or prepending HTML to the doclet.description property. Worked for me.
exports.defineTags = function(dictionary) {
dictionary.defineTag('routeparam', {
mustHaveValue: true,
mustNotHaveDescription: false,
canHaveType: true,
canHaveName: true,
onTagged: function(doclet, tag) {
if (!doclet.routeparams) {
doclet.routeparams = [];
}
doclet.routeparams.push({
'name': tag.value.name,
'type': tag.value.type ? (tag.value.type.names.length === 1 ? tag.value.type.names[0] : tag.value.type.names) : '',
'description': tag.value.description || '',
});
}
});
};
exports.handlers = {
newDoclet: function(e) {
const parameters = e.doclet.routeparams;
if (parameters) {
const table = tableBuilder.build('Route Parameters', parameters);
e.doclet.description = `${e.doclet.description}
${table}`;
}
}
}
Feel free to check out my repo to see how I did it https://github.com/bvanderlaan/jsdoc-route-plugin

How can I dynamically generate a list of filenames for use in a task with Grunt?

I'm using load-grunt-config and grunt-prompt, and I'm developing an init task, which copies some php templates between two folders.
Right now the template filenames are hardcoded, but I'd rather have grunt scan the right folder and provide the filenames dynamically.
I've tried using grunt.file.expand, but I'm unable to get it to work. Is it possible to scan a folder and return an array (or object, not sure what you'd call it) of filenames in the format that grunt-prompt expects?
// -------------------------------------
// Grunt prompt
// -------------------------------------
module.exports = {
// ----- Initialization prompt ----- //
init: {
options: {
questions: [{
// Set the authors name
config: 'init.author.name',
type: 'input',
message: 'What is your name?'
}, {
// Set the name of the project
config: 'init.project.name',
type: 'input',
message: 'What is the name of your project?'
}, {
// Select templates to be used
config: 'init.php.templates',
type: 'checkbox',
message: 'Which templates do you want to use?',
choices: [{
name: '404.php',
checked: false
}, {
name: 'archive.php',
checked: false
}, {
name: 'comments.php',
checked: false
}]
}]
}
}
};
By the way, I have found this answer: https://stackoverflow.com/a/22270703/1694077, which relates to the problem. But it does not go into detail about how one would specifically address this problem. Also, I need more specific syntax than just an array of filenames:
[{
name: '404.php'
}, {
name: 'archive.php'
}]
Basic Principle
Here's a way to do it that uses Grunt's file matching capabilities to get a list of files. The following code will seek templates in a subdirectory named templates. You just need to put your php files there and the script will find it. Note I've omitted the use of load-grunt-config since it is not a factor in the specific problem of getting a list of files.
The key is to use grunt.file.expand to get the files.
module.exports = function (grunt) {
// List all files in the templates directory.
var templates = grunt.file.expand({filter: "isFile", cwd: "templates"},
["*"]);
// Make actual choices out of them that grunt-prompt can use.
var choices = templates.map(function (t) {
return { name: t, checked: false};
});
grunt.initConfig({
prompt: {
init: {
options: {
questions: [{
// Set the authors name
config: 'init.author.name',
type: 'input',
message: 'What is your name?'
}, {
// Set the name of the project
config: 'init.project.name',
type: 'input',
message: 'What is the name of your project?'
}, {
// Select templates to be used
config: 'init.php.templates',
type: 'checkbox',
message: 'Which templates do you want to use?',
choices: choices
}]
}
}
}
});
grunt.task.loadNpmTasks("grunt-prompt");
grunt.registerTask("default", ["prompt"]);
};
You could use something more sophisticated than "*" as a pattern. For instance, if you are going to have other types of files there that you don't want to list "*.php" would be indicated. I also use isFile for the filter option to avoid listing directories. And I use cwd to change the working directory to templates before listing the files, which means the file names returned do not include templates/ in their name. It would also be possible to do this instead:
var templates = grunt.file.expand({filter: "isFile"}, ["templates/*"]);
and get a list of files that include the templates/ directory in their name.
With load-grunt-config
By default, load-grunt-config wants a package.json file (because it calls load-grunt-tasks). This is what I've used:
{
"dependencies": {
"load-grunt-config": "^0.8.0",
"grunt-prompt": "^1.1.0",
"grunt": "^0.4.4"
}
}
The Gruntfile.js becomes:
module.exports = function (grunt) {
grunt.registerTask("default", ["prompt"]);
require('load-grunt-config')(grunt);
};
And then in grunt/prompt.js you need this:
module.exports = function(grunt) {
// List all files in the templates directory.
var templates = grunt.file.expand({filter: "isFile", cwd: "templates"},
["*"]);
// Make actual choices out of them that grunt-prompt can use.
var choices = templates.map(function (t) {
return { name: t, checked: false};
});
return {
init: {
options: {
questions: [{
// Set the authors name
config: 'init.author.name',
type: 'input',
message: 'What is your name?'
}, {
// Set the name of the project
config: 'init.project.name',
type: 'input',
message: 'What is the name of your project?'
}, {
// Select templates to be used
config: 'init.php.templates',
type: 'checkbox',
message: 'Which templates do you want to use?',
choices: choices
}]
}
}
};
};
Here is a short code to list the files in a dir:
var fs = require("fs")
var files = [];
var list = function (path) {
fs.readdirSync(path).forEach(function (file) {
if(fs.lstatSync(path + '/' +file).isDirectory())
list(path + '/' +file);
else
files.push({name: file});
});
}
list(YOUR_PATH)
console.log(files)
In your example :
var fs = require("fs")
var files = [];
var list = function (path) {
fs.readdirSync(path).forEach(function (file) {
if(fs.lstatSync(path + '/' +file).isDirectory())
list(path + '/' +file);
else
files.push({name: file});
});
}
list(YOUR_PATH)
module.exports = {
// ----- Initialization prompt ----- //
init: {
options: {
questions: [{
// Set the authors name
config: 'init.author.name',
type: 'input',
message: 'What is your name?'
}, {
// Set the name of the project
config: 'init.project.name',
type: 'input',
message: 'What is the name of your project?'
}, {
// Select templates to be used
config: 'init.php.templates',
type: 'checkbox',
message: 'Which templates do you want to use?',
choices: files
}]
}
}
};
There is another way to do this that uses a different Grunt utility method than what's listed in the accepted answer. Its worth mentioning because it exposes the options object for the "glob" npm package that Grunt uses internally.
See README.md for the version of glob used by your version of Grunt for more info (check its package.json file). I first looked at the most current glob and was puzzled by the fact that Grunt had no grunt.file.globSync() method. I eventually realized Grunt was using an earlier version of glob, after which {sync: true} was removed from the options object and replaced by the globSync() function I'd been looking for.
NOTE: grunt.file.glob() does not take a src array as grunt.file.expand() does, but you can use braces to effectively encode an array:
var options = { ... };
var results =
grunt.file.glob(
'{first/path/**/*,second/path/**/*}',
options
);
Here's an example block showing how I use this in jade processing, which I seem to recall does not handle expand blocks natively for some reason. The options:
allow wildcards to match dot prefixed files
disable unnecessary stat calls for non-wildcard glob pattern path nodes
disables sorting since I don't care about input order
disables de-duping because I know my pattern matches each file exactly once
requests an empty array rather than an array if no files match
var filesObj = { };
var configObj = {
build: {
options: {
pretty: true,
data: {
titleStr: 'This is a title from data'
}
},
files: filesObj
}
};
// Get source and build output directory roots from config
var srcPrefix =
new RegExp('^' + grunt.config.get('sourceAssets'));
var buildPrefix = grunt.config.get('buildAssets');
// Call once per glob match to populate files object.
function addJadeFile(srcPath) {
// Handle glob output on Windows by denormlalizing path.
srcPath = srcPath.replace(/\/g, '/');
// Extract common path suffix and change file extension
var relPath =
srcPath.replace(srcPrefix, '..').replace(/.jade$/, '.html');
// Prepend destination path prefix and a property
// to files sub-object of config object.
filesObj[buildPath + relPath] = srcPath;
}
grunt.file.glob(
appConfig.source.client + '/**/*.jade',
{ sync: true, stat: false
strict: true, dot: false,
nonull: false, nodir: true,
nosort: true, nounique: true }
).forEach(addJadeFile);
return configObj;
P.S. Both grunt.file.glob() and grunt.file.expand() are generally preferable over traversal methods from the fs package because glob keeps a traversal cache.
It's also good to be aware of glob's traversal cache if you are looking for files that may have been created by a previous build step, but not before other tasks first traversed their creation location and populated that cache. I've not run into this yet, but be aware of it in case you need to find out how to purge or disregard the cache in such a corner case.

Categories