Is it possible to import named exports dynamically?
I have a file, banana.js with hundreds of named exports. Id like to import them on demand. Is this possible? And if it is, will it only load that export and not all?
I know its possible to import them dynamically from individual files but I want them in the same file.
Example below..
// banana.js
export const banana_1 = {
..
}
export const banana_2 = {
..
}
// main.js
const currentPage = 1
async getSomething(){
let { `banana_${currentPage}` } = await import('./banana.js');
const foo = `banana_${currentPage}`
}
Fyi im using Vue.js
From what I know, you might have to use require('./banana.js') here. Please note that require is synchronous, so need for await. If you use eslint and it forbids usage of require, just add // eslint-disable-line to the end of that line. BTW, I don't know Vue at all.
About the object you import, you should probably make an array instead of suffixing each export with a number.
Edit: I just realized that dynamic imports are a thing not only possible with require, so ignore the first paragraph.
Based on your response to my question I offer the following solution. I understand not wanting to deploy and use a full database solution for the sake of hot loading some JSON objects. I'm unsure of your use case and other details related to your project.
You can use a self contained database like SQLite. Instead of setting up a NoSQL or SQL server. You can instead just store what you need to the SQLite file. Since Vue requires Node.js you can use the Node.js SQLite3 library https://www.npmjs.com/package/sqlite3.
I hope this helps.
Related
I support a relatively complex legacy codebase, but am looking to modernise it a little by bringing in Webpack so that we'd have import & export capabilities in JS.
The problem I'm having is that we use a global object called App where we define and add different properties depending on the page. So for example we have the following file where we instantiate App (loaded on all pages):
app.js
const App = (() => {
const obj = {
Lib: {},
Util: {},
// etc
}
return obj;
})();
Then in another file we add to App.Lib just for the specific page that needs it:
lazyload.js
App.Lib.Lazyload = (() => {
// lazyload logic
})();
We simply concatenate the files during the bundling process, but obviously this is not ideal as none of the files have no knowledge of what goes on outside of it.
Exporting only seems to work for the top level object (where the object is defined), so anything I add to it elsewhere cannot be exported again. For example if I add export default App.Lib.Lazyload; at the end of lazyload.js and then try to import it elsewhere it will not import the Lazyload property.
Is there any way to get this to work without major refactor? If not, would you have any suggestions about the best way to handle it?
I don't think you can import Object.properties in JS. If you want to bundle specific packages (say Lazyload) for packages that need them, you might try:
//lazyload.js
export const LazyLoad = {
//lazyload logic
}
then somewhere else...
import {LazyLoad} from 'path/to/lazyload.js';
// assuming App has already been created/instantiated
App.Lib.Lazyload = LazyLoad;
Using Export Default...
//lazyload.js
const LazyLoad = {};
export default LazyLoad;
then...
import LazyLoad from 'path/to/lazyload.js';
App.Lib.LazyLoad = LazyLoad;
You can find help with Imports and Exports at MDN.
In the getServerSideProps function of my index page, I'd like to use a function foo, imported from another local file, which is dependent on a certain Node library.
Said library can't be run in the browser, as it depends on "server-only" modules such as fs or request.
I've been using the following pattern, but would like to optimize it. Defining foo as mutable in order to have it be in scope is clunky and seems avoidable.
let foo;
if (typeof window === "undefined") {
foo = require("../clients/foo");
}
export default function Index({data}) {
...
}
export async function getServerSideProps() {
return {
props: {data: await foo()},
}
}
What would be the best practice here? Is it somehow possible to leverage ES6's dynamic import function? What about dynamically importing within getServerSideProps?
I'm using Next.js version 9.3.6.
Thanks.
UPDATE:
It seems as if Next.js's own dynamic import solution is the answer to this. I'm still testing it and will update this post accordingly, when done. The docs seem quite confusing to me as they mentionn disabling imports for SSR, but not vice versa.
https://nextjs.org/docs/advanced-features/dynamic-import
When using getServerSideProps/getStaticProps, Next.js will automatically delete any code inside those functions, and imports used exclusively by them from the client bundle. There's no risk of running server code on the browser.
However, there are a couple of considerations to take in order to ensure the code elimination works as intended.
Don't use imports meant for the server-side inside client-side code (like React components).
Ensure you don't have unused imports in those files. Next.js won't be able to tell if an import is only meant for the server, and will include it in both the server and client bundles.
You can use the Next.js Code Elimination tool to verify what gets bundled for the client-side. You'll notice that getServerSideProps/getStaticProps gets removed as do the imports used by it.
Outside of getServerSideProps/getStaticProps, I found 2 fairly similar solutions.
Rely on dead code elimination
In next.config.js:
config.plugins.push(
new webpack.DefinePlugin({
'process.env.RUNTIME_ENV': JSON.stringify(isServer ? 'server' : 'browser'),
}),
);
export const addBreadcrumb = (...params: AddBreadcrumbParams) => {
if (process.env.RUNTIME_ENV === 'server') {
return import('./sentryServer').then(({ addBreadcrumb }) => addBreadcrumb(...params));
}
return SentryBrowser.addBreadcrumb(...params);
};
Note that some for reason I don't understand, dead code elimination does not work well if you use async await, or if you use a variable to store the result of process.env.RUNTIME_ENV === 'server'. I created a discussion in nextjs github.
Tell webpack to ignore it
In next.config.js
if (!isServer) {
config.plugins.push(
new webpack.IgnorePlugin({
resourceRegExp: /sentryServer$/,
}),
);
}
In that case you need to make sure you will never import this file in the client otherwise you would get an error at runtime.
You can import the third party library or a serverside file inside getServerSideProps or getInitialProps since these functions run on server.
In my case I am using winston logger which runs on server only so importing the config file only on server like this
export async function getServerSideProps (){
const logger = await import('../logger');
logger.info(`Info Log ->> ${JSON.stringify(err)}`);
}
You can also import library/file which has default export like this
export async function getServerSideProps(context) {
const moment = (await import('moment')).default(); //default method is to access default export
return {
date: moment.format('dddd D MMMM YYYY'),
}
}
I'm developing a package for the Atom text editor, and in order for that package to work, it reads some JSON files that act as a simple database. Also, those JSON files should be periodically updated with data obtained from a public spreadsheet, thus they must be written to.
The problem, however, is that using fs.writeFile, paths are relative to the user's home folder, not to the package folder. While I could use a path like .atom/packages/package-name/file.json, to my limited knowledge of Atom packages, that does not feel like a good practice (or is it?).
Are there any other solutions?
I don't see a problem using relative paths in your script like so:
const pathToJson = path.resolve(__dirname, 'file.json`);
// next: write data to JSON
However, if you really want to use absolute paths, there are several options.
Atom API
You can use the Atom API inside your package, which exposes the resolvePackagePath() method:
const packagePath = atom.packages.resolvePackagePath('name-of-your-package');
const pathToJson = path.resolve(packagePath, 'file.json`);
// next: write data to JSON
You can combine this with the following snippet to retrieve the package name from the manifest:
const { name } = require('./package.json');
3rd party package
The atom-read-manifest package allows you to do the same without having to specify the name of your package – but that's a question of personal taste, I guess.
const { readManifest } = require('atom-read-manifest');
// or use readManifestSync
(async () => {
const { name } = await readManifest();
const packagePath = atom.packages.resolvePackagePath(name);
const pathToJson = path.resolve(packagePath, 'file.json`);
// next: write data to JSON
})();
One final note: If your package is writing the data to the JSON, there are probably better ways to achieve the same. You need to consider that updating the package will overwrite your old file.json. Personally, I'd prefer to write the data to Atom's localStorage (or IndexedDB). If you prefer writing the data to a JSON file, then you should probably save it to .atom/storage instead.
I've been trying to use the import() function to import something which is dynamic at runtime. I would think that as long as I create an entry for the file, webpack could be smart enough to import() the correct module, but that doesn't seem to be the case.
Does anyone know of a way to chunk off an entry and use the import() syntax, feeding it a variable, and have it work at runtime?
A simple example of the root problem is as follows:
// works
import( './a.js' ).then(() => console.log('it worked'));
// something is a dynamic variable that changes at runtime
const something = './a.js';
// does not work, even with this simplistic example
import( something ).catch(() => console.log('it did not work'));
It does not work because, although it is called "dynamic import" it is does not follow what the word means. The idea on "dynamic" import is to be able to import something dynamically at runtime, but here it is caveat: the module to be imported has to be known.
Since webpack does static analysis to do the lazy loading on these import() statements, everything has to be known and predictable, otherwise webpack would not be able to create async chunks on the fly. That is why adding a variable to the import does not work.
Yeah, this bit of webpack is weird. Truly dynamic import doesn't work.
We can put string template inside the import statement, but putting variable name raises Dependency warning.
This is the closest I could achieve - put if conditions or switch case or a string template and then write the import in it.
Something like -
const getModule = (filename) => {return import(`directoryPath/${fileName}`);}
OR
const something = 'a.js';
if (something === 'a.js') {
return import(`./${something}`); // promise
} else if (something === 'b.js'){
.........
}
There's this thing with dynamic imports - webpack bundles all possible files in these conditions which is not cool.
Is it necessary to have typing for every JS library you use in typescript?
if not, then how to get rid of errors and make use of a library which doesn't have available definition files
import { NotificationContainer, NotificationManager } from 'react-notifications';
can't find typings for react-notifications
It's not necessary. For your example, you create a file react-notifications.d.ts (you can call it anything you like as long as the extension is .d.ts but it makes sense to name it consistently):
declare module "react-notifications" {
const NotificationContainer: any;
const NotificationManager: any;
}
That's a near-minimal starting point. But you could go a little further and improve on those any declarations. They are a temporary cop-out.
An alternative to #Daniel Earwicker answer, as long as you are in a commonJS environment (and I guess you are using webpack) is to simply require the library using node require
const rn = require('react-notifications')
then use rn.NotificationContainer directly or import NotificationContainer = rn.NotificationContainer