Is it possible to get data from server side's config file on client by getServersideProps() in Next.js? How to put data in props? Or how to get it on client in other way?
I have tried publicRuntimeConfig in next.config.js, but it is undefined and I don't know why...
It's hard to tell exactly what's going on but I have one idea from experience: You need to make sure you're calling nextJSApp.prepare() before any modules using next/config are included.
As an example,
// SomeComponent.tsx
import { getConfig } from 'next/config'
const config = getConfig()
export interface X { ... }
// server.ts
import { X } from './SomeComponent'
app.prepare().then(...)
This fails because module are loaded first and the config hasn't been initialized until app.prepare has been completed.
The solution for this is to either use TypeScript's import(...) syntax if you just need a type or use node's require for dynamic resolution during runtime.
Related
I am trying to develop a library similar to Axios for both client and server side.
I was hoping that the tree shaking feature from webpack would help me to remove dead code from imports when used in client side, but it doesn't. Here is a short example of the code:
import http from "http";
import https from "https";
export class FetchClient {
static request(...) { ... }
};
export class FetchServer {
static request(...) { ... }
}
When I only import FetchClient, webpack still includes the code for "http" and "https" in my bundle, which makes the bundle ~100KB larger than expected, eventhough it is not used in this code path.
Also I looked at the code for Axios, it seems they are using conditional require instead of import (see the adapter code part). Would the trick be to load conditionaly and therfore asynchronously the http and https dependencies? In that case the code would still be in my bundle, which is somethign I would like to avoid.
Note: I am using the TerserPlugin (minimizer), "sideEffects": false in my packages.json and modules all the way.
EDIT:
I managed to go around my issue, by using the EnvironmentPlugin webpack plugin and splitting my code in 3 files: 2 files containing each implementation (for client and server) and the main file containing the following logic:
let adapter = null;
if (process.env.EXEC_ENV === "node") {
adapter = (await import("./adapter/node.http.js")).default;
}
else if (process.env.EXEC_ENV === "web") {
adapter = (await import(/* webpackMode: "eager" */"./adapter/window.fetch.js")).default;
}
I find it quite strange that webpack is not able to do this by himself, I thought that's what the sideEffects flag was for. Maybe the static analyzer is not smart enough?
import https from "https";
Importing like this will import all the files. Since it does not know, there might be some global initialization. Same time HTTP, and HTTPS module will not be available on client-side. So it has to be browsify.
Axios also include follow-redirects, which includes both HTTP and HTTPS module.
Axios:
transport = isHttpsProxy ? httpsFollow : httpFollow;
This is not condition require, this is a conditional return. adaptor pattern.
I trying to convert a nodejs project to TypeScript and while mostly I did not faced really difficult obstacles during this process, the codebase has few gotchas like this, mostly in startup code:
function prepareConfiguration() {
let cloudConfigLoader = require('../utils/cloud-config');
return cloudConfigLoader.ensureForFreshConfig().then(function() {
//do some stuff
});
}
I may be need just an advice on which approach for refactoring this has less code changes to be made to make it work in TypeScript fashion.
In response to comments, more details:
That require loads the node module, not a JSON file. From that module the ensureForFreshConfig function contacts with a cloud service to load a list of values to rebuild a configuration state object.
Problem is that mdule was made in standard node approach of "module is isngleton object" and its independencies include auth component that will be ready only when the shown require call is made. I know it is not best a way to do so..
Typesript does not allow "mport that module later" except with dynamyc import which is problematic as mentiond in comment.
The "good" approach is to refactor that part of startup and make the ensureForFreshConfig and its dependency to initiate its ntenras on demand via cnstructors.. I just hoped ofr some soluiton to reduce things to be remade durng this transition to the TypeScript
import { cloudConfigLoader } from '../utils/cloud-config'
async function prepareConfiguration() {
await cloudConfigLoader.ensureForFreshConfig()
// do some stuff
// return some-stuff
}
The function is to be used as follows.
await prepareConfiguration()
So I was recently hacking on a large Typescript project (https://github.com/BabylonJS/Babylon.js) and I noticed that they don't ever have to import anything, they just use their namespace and the rest is (seemingly) magic.
It got me thinking that I would like to use something similar for myself, so I started a simple typescript project to try it out.
tsconfig.json
{
"compilerOptions": {
"baseUrl": "src",
"outFile": "server.js"
}
}
src/main.ts
module Test {
console.log('Main started')
const server:Server = new Server()
}
src/Server.ts
// import * as http from 'http'
module Test {
export class Server {
constructor() {
console.log('Server initialized')
}
}
}
If I build this Typescript project then I get output like the following:
// import * as http from 'http'
var Test;
(function (Test) {
var Server = /** #class */ (function () {
function Server() {
console.log('Server initialized');
}
return Server;
}());
Test.Server = Server;
})(Test || (Test = {}));
var Test;
(function (Test) {
console.log('Main started');
var server = new Test.Server();
})(Test || (Test = {}));
So far, so good. The trouble is that I want to take advantage of some external modules, in this case namely the http module, so I uncomment the import line above and now Typescript reports:
src/server/Server.ts(1,1): error TS6131: Cannot compile modules using option 'outFile' unless the '--module' flag is 'amd' or 'system'.
Node uses the commonjs module system, so obviously setting either of those flags isn't going to help me much. I have none the less tried them as well as various other combinations of flags, but to no avail. I noticed that BabylonJS doesn't really use external imports like that, opting instead to declare them as external and provide them globally at execution time as script tags. Is there maybe an analogue to that for Node?
You can't have these two things at the same time, namely
How can I avoid always having to import my own code in Typescript?
and
I want to take advantage of some external modules
You can avoid imports only by not using external modules, and the result will be one giant script file that can use external dependencies only as globals created by scripts loaded via script tag, as you already noticed.
The language does not allow you to use external modules when you do this. If you have an import of external module at the top level, your file becomes a module and there is no way it could use code from your other files without importing them. And having import of external module inside a namespace is not allowed AFAIK.
That said, I don't think your question - "How can I avoid always having to import my own code in Typescript?" - has a valid premise. CommonJS module system is the solution for preventing large projects from becoming unmaintainable mess. It does not matter if some part of a project is your own code or some external dependency - if it's a separate part with well-defined interface, it should be packaged and consumed as a module.
The solution that worked for me is this:
Server.ts
declare var http: any;
namespace Test {
export class Server {
constructor() {
console.log('Server initialized')
const server = http.createServer()
server.listen()
}
}
}
Then I simply provide http at runtime, for example by prepending a var http = require('http') to the output. Thanks to artem for a nudge in the right direction.
I have the following reference import { STORE } from "../data/store"; It's part of a react project and works as expected.
However I need to run some code separately over STORE and accessed it the same way as it is in the react project, but ran via ts-node. When I try to access STORE by let data = STORE[videoId].labels;, with videoId set as "home" I get TypeError: Cannot read property 'home' of undefined.
Might anyone know what I'm missing --- must be something specific to ts-node...? Thanks!
In case someone arrives on this page because of imports issue, like I did, here's what my problem was:
I had three files : index.ts, File.json and File.ts
In index.ts:
import { thing } from "./File";
was working well in VS Code: I had auto-completion for thing and everything.
But when running ts-node index.ts, thing was undefined!
When I remembered that I had the json file, I just renamed File.ts to File.module.ts and changed the import to:
import { thing } from "./File.module";
and it was sorted.
It sounds like the property STORE is undefined..
If you change import { STORE } from "../data/store"; to import * as STORE from "../data/store" does it work?
I'm not sure if this extreme edge case or something but I cannot seem to find straight forward documentation on how to do this (or I'm just really not understanding what is available):
I am developing an ionic application and as part of that I need to use the ALKMaps JavaScript library (which is similar to Google Maps API). To do so, I created a local npm module and within that I created a alkmaps.d.ts file as recommended by https://www.typescriptlang.org/docs/handbook/declaration-files/by-example.html#objects-with-properties). However, I cannot seem to figure out how to properly import it into my angular code. The same document suggests that using <reference path=''> tags is not good but that is the only thing that seems to satisfy the tsc compiler.
My declaration file, alkmaps.d.ts, looks like (inside excluded for brevity):
declare namespace ALKMaps {
export class Map { ... }
...
}
And I was trying to import it into a file like:
import { ALKMaps } from 'alkmaps'; // Error: File '.../alkmaps.d.ts' is not a module
I also tried the following but got the same error.
import ALKMaps = require('alkmaps');
Using the reference tag seems to work within this module but then the project that utilizes this module still throws the "is not a module" error (that might warrant a separate question)
From https://github.com/Microsoft/TypeScript/issues/11420 I found the idea of using export = ALKMaps or export as namespace ALKMaps but adding those to my declaration file resulted in different errors instead.
Can anyone please explain in a straightforward way how to use declaration files representing external JS libraries in a typescript node module?
This is how I was able to get alkMaps into my Angular 2 app
Insert the script into the index.html file.
Declare an ALKMaps variable in the component that you are adding the map
imports .....
declare let ALKMaps : any;
#Component({
selector: 'show-map',
templateUrl: 'show-map.component.html'
})
export class ShowMapComponent implements Oninit{
map : any;
constructor() {
}
ngOnInit() {
ALKMaps.APIKey = "apiKey";
this.map = new ALKMaps.Map("map", {displayProjection: new ALKMaps.Projection("EPSG:4326")});
}
}
This will get the map to display and you can put different layers on the map, however the map does not display correctly. #Mike, if you were able to get further than this, will you please comment?
EDIT: The tiles on the image were elongated and not connected. After inspecting the css the main.css, after building, set a global property on the img element to:
img {
max-width:100%
}
The tiles for the map are originally set to 256% for the width. To correct the element, I changed the property for img in the style sheet.
show-map {
img {
max-width: 256%
}
}