Node process object made available to browser client code - javascript

I'm trying to understand how webpack uses DefinePlugin. I have:
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('development'),
}),
and a function:
export const foo = () => {
console.log(process)
console.log(process.env.NODE_ENV)
}
window.foo = foo
when I print foo, I see the following in my browser console:
ƒ foo() {
console.log(process);
console.log("development");
}
It seems like the variable "development" was injected while webpack was compiling the input file. At the same time webpack also injected the process object into the JavaScript code, and the browser did print out the process object when foo was called:
{title: "browser", browser: true, env: {…}, argv: Array(0), nextTick: ƒ, …}
My question is, how can the process object, which is a Node concept, be made available to the browser?
In fact, if I do:
window.process = process
I can use process.nextTick right inside the browser console! I thought the nextTick function was a Node-specific implementation! Could anybody explain this?
Thank you!

As mentioned here https://webpack.js.org/configuration/node/#node-process, the webpack can make polyfills for different node functions, but it appears as the node.process is a "mock";
"mock": Provide a mock that implements the expected interface but has little or no functionality.
Have you tested it to see if it actually works? It might just be an empty shell.
If it works I assume that the plugin actually uses something like node-process as shown in this blog-post: http://timnew.me/blog/2014/06/23/process-nexttick-implementation-in-browser/
Copied from that blogpost:
process.nextTick = (function () {
var canSetImmediate = typeof window !== 'undefined'
&& window.setImmediate;
var canPost = typeof window !== 'undefined'
&& window.postMessage && window.addEventListener;
if (canSetImmediate) {
return function (f) { return window.setImmediate(f) };
}
if (canPost) {
var queue = [];
window.addEventListener('message', function (ev) {
var source = ev.source;
if ((source === window || source === null) && ev.data === 'process-tick') {
ev.stopPropagation();
if (queue.length > 0) {
var fn = queue.shift();
fn();
}
}
}, true);
return function nextTick(fn) {
queue.push(fn);
window.postMessage('process-tick', '*');
};
}
return function nextTick(fn) {
setTimeout(fn, 0);
};
})();
It is a bit hard to know for sure from the info you provided. If it truly works I think it is very likely you have Browserify enabled in your node app. Perhaps you find some of what you need here: https://webpack.js.org/loaders/transform-loader/#src/components/Sidebar/Sidebar.jsx
Hopefully you find this answer somewhat helpful.
The bottom line is that I believe it is a polyfill from somewhere.

How webpack deals with Node globals and webpack.DefinePlugin are actually two different concerns.
Default node globals are globally injected, while constants defined in webpack.DefinePlugin are physically replaced one by one trough all the codebase.
eg:
// config
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('development'),
'process.env.MY_VAR': {foo: JSON.stringify('bar')},
}),
// source
console.log('process.env.NODE_ENV', process.env.NODE_ENV);
console.log('process.env.MY_VAR', process.env.MY_VAR);
console.log('process.env', process.env);
console.log('process', process);
// compiled
console.log('process.env.NODE_ENV', "development");
console.log('process.env.MY_VAR', __webpack_require__.i({"foo":"bar"}));
console.log('process.env', process.env);
console.log('process', process);
Note that process.env.NODE_ENV and process.env.MY_VAR physically are replaced, while process.env and process keep their reference to the injected process mock.
But webpack.DefinePlugin is also able to override the mocked process object (or just part of it): a lot of power which implies the risk of getting unexpected behaviours.
From Webpack docs:
When defining values for process prefer 'process.env.NODE_ENV': JSON.stringify('production') over process: { env: { NODE_ENV: JSON.stringify('production') } }. Using the latter will overwrite the process object which can break compatibility with some modules that expect other values on the process object to be defined.
eg:
// config
new webpack.DefinePlugin({
'process': JSON.stringify('override'),
'process.env.NODE_ENV': JSON.stringify('development'),
}),
// source
console.log('process', process);
console.log('process.env', process.env);
console.log('process.env.NODE_ENV', process.env.NODE_ENV);
// compiled
console.log('process', "override");
console.log('process.env', "override".env); // [boum!]
console.log('process.env.NODE_ENV', "development"); // [replaced by DefinePlugin!]

This doesn't directly answer this question, but it was the solution I needed. I was trying to access process in code that webpack compiled, intending the compiled code to be run in a NodeJS environment rather than in the browser. The process variable doesn't exist on window, but on global.
The solution was to set the target in the webpack config.
webpack.config.js
const config = {
// ...
target: 'node',
// ...
};
module.exports = config;
This removes the window mock.

see
https://webpack.js.org/configuration/node/#node
These options configure whether to polyfill or mock certain Node.js globals and modules. This allows code originally written for the Node.js environment to run in other environments like the browser.
This feature is provided by webpack's internal NodeStuffPlugin plugin. If the target is "web" (default) or "webworker", the NodeSourcePlugin plugin is also activated.

I am going to assume you are experimenting with this on your local webpack dev server and not in your production build. Webpack dev server utilizes websockets for real time communication between the underlying node processes and the front-end bundle. If you wanted to utilize this in a production environment you would need to set up a socket between your front end JS and your node instance to send commands. From a security perspective it sounds like a nightmare, but may have some valid use cases.

Related

Implementing require in the absence of node

Modules generally start something like this
(function(root, factory)
{
/* globals define */
if (typeof define === 'function' && define.amd)
{
// AMD. Register as an anonymous module.
define([], factory);
}
else if (typeof module === 'object' && typeof exports !== 'undefined')
{
// Node. Does not work with strict CommonJS, but
// only CommonJS-like environments that support module.exports,
// like Node.
module.exports = factory();
}
else
{
// Browser globals (root is window)
root.Papa = factory();
}
I'm trying to implement require to handle node style CommonJS-like modules.
Find the package folder, parse package.json to learn the entrypoint and dependencies, recursively descent with a shared cache to load dependencies... that stuff works.
But having loaded the script for a module, I'm having trouble executing it in such a way as to have it populate module.exports
This will all end up running on Jint (a JS interpreter) so node isn't supplying the usual furniture, I have to build it all myself. There's no step-debug with Jint so I'm using node from VS Code and faking Jint like this.
import * as fs from "fs";
var code = fs.readFileSync("node_modules/papaparse/papaparse.js").toString();
let x = 3;
console.log(eval("x*x"))
let result = eval(`
let module = { exports: "dunno" };
${code}
console.log(module.exports);
`);
This is in a file test.js and package.json nominates this file as main and specifies a type of module. It launches, reads the module code, creates module and runs the code, which promptly complains that it Cannot set properties of undefined (setting 'Papa').
Looking at the snippet above, that tells us it's executing the last else clause, which means it's not seeing module. I thought it might be some sort of scope thing for eval which is where this came from
let x = 3;
console.log(eval("x*x"))
but that duly writes 9 to the console so the question is why module isn't in scope.
This is one of those JavaScript closure weirdnesses; can anyone guide me on how to provide the module object so that the second clause will take effect populating module.exports with the result of factory()?
I know I said it's running in the absence of Node, but I'm debugging it using Node mostly because that's what you get when you launch a js file in VS Code. As already mentioned the production target is Jint. The debug environment is as close an approximation as I can manage, and I'm refraining from using facilities that won't be available in production.
In the debug environment I've created a package.json file that governs whether it's treated as CommonJS or ES6. Experiments show that the production environment behaves like more ES6 than commonjs. The trouble is that most packages expect that they will run either in a browser or in node. They don't consider the possibility of another headless environment. The code above decides it's browser hosted and tries to to install Papa in a DOM that isn't there.
The solution is to wrap the module like so (I'm constructing a string in C#).
string wrapped =
#"(function () {
var module = { exports: { } }; " +
jsSource + #"
var strays = Object.keys(this).filter(itemName => itemName !== 'module');
if (strays.length === 1)
module.exports = this[strays[0]]
else
strays.forEach(itemName => module.exports[itemName] = this[itemName]);
return module.exports;
}).apply({});";
Wrapping it in an anonymous function and using apply to set this for the call allows the "DOM write" to succeed, and a little follow-up code looks for these strays, gathering them up into module exports before returning to normal operation.

How do I initialize a webworker in NextJS 10?

I have a Next 10 project where I am trying to use WebWorkers. The worker is being initialized like so:
window.RefreshTokenWorker = new Worker(new URL('../refreshToken.worker.js', import.meta.url))
I also have the Worker defined as
self.addEventListener('message', (e) => {
console.info("ON MESSAGE: ", e)
// some logic with e.data
})
Its also being called like this:
const worker = getWorker() // gets worker that is attached at the window level
worker.postMessage('start')
My next.config.js file is defined as
const nextConfig = {
target: 'serverless',
env: getBuildEnvVariables(),
redirects,
rewrites,
images: {
domains: []
},
future: { webpack5: true },
webpack (config) {
config.resolve.alias['#'] = path.join(__dirname, 'src')
return config
}
}
// more definitions
module.exports = nextConfig
The issue I have is the console.info in the Web Worker definition does not receive the message being sent from postMessage on the build version (yarn build && yarn start) but it does on the dev version (yarn dev). Any ways to fix this?
This is not a solution. But can be a messy way to do the job. This turned out to be a nightmare for me.
I have the same setup as yours. I was initializing web worker as you have shown in your question. I got this idea from the nextjs doc itself: https://nextjs.org/docs/messages/webpack5
const newWebWorker = new Worker(new URL('../worker.js', import.meta.url))
Everything working correctly when I work in dev mode. it is picking up the worker.js file correctly and everything looks alright.
But when I build the nextjs and try it, then web worker won't work. When I dive deeply into the issues, I found out that the worker.js chunk file is created directly under the .next folder. It should come under .next/static/chunk/[hash].worker.js ideally.
I could not resolve this issue in a proper way.
So what i did, i placed my worker.js file directly under public directory. I put my worker.js file transpiled and optimized and put the code in the public/worker.js file.
After this, I modified the worker initialization like this:
const newWebWorker = new Worker('/worker.js', { type: 'module' });
it is working in the production build now. I will report once I get a cleaner solution for this.

Best way to create a "fresh and empty" Node.js JavaScript context within the same process?

Question for Node.js and v8 experts.
I'm developing a new version of the Siesta testing tool.
By default, Siesta runs every test in the newly created Node.js process. However, I'd like to avoid the overhead of spawning a new process and instead provide the ability to run the test in the empty JavaScript context.
Such context can be created with the built-in vm module. However, the context created in this way is an empty JavaScript context, not an empty Node.js context. For example, it does not have global variable process:
> require('vm').runInNewContext('process')
evalmachine.<anonymous>:1
process
^
Uncaught ReferenceError: process is not defined
at evalmachine.<anonymous>:1:1
at Script.runInContext (vm.js:143:18)
at Script.runInNewContext (vm.js:148:17)
at Object.runInNewContext (vm.js:303:38)
at REPL30:1:15
at Script.runInThisContext (vm.js:133:18)
at REPLServer.defaultEval (repl.js:484:29)
at bound (domain.js:413:15)
at REPLServer.runBound [as eval] (domain.js:424:12)
at REPLServer.onLine (repl.js:817:10)
>
So question is - what is the best way to create a fresh and empty Node.js context within the same process? I'd expect such context to have all regular globals, like process, require etc. Plus, I'd expect such context to have a separate and initially empty modules cache, so that even if some module is loaded in the main context, it will be loaded again in the new context.
Of course I could map the globals from the main context to the new context, but that would mean those globals are shared between contexts, and I'm aiming for context isolation. Plus the modules cache will be shared as well.
I believe the difference between JavaScript and Node.js context is that the latter is initialized with a certain script. Is it possible to obtain the sources of that script somehow and execute it in the new context?
Thank you!
This is what happens when NodeJS loads a new module:
(function(exports, require, module, __filename, __dirname) {
// Module code actually lives in here
});
They use what they call a Module Wrapper
So you would have to do something similar using VM
require('vm').runInNewContext('the code you are running', {
module: // your empty module or a wrapper
exports: // a reference to the module.exports
require: // your empty require or a wapper
__filename: // your __filename
__dirname: // your __dirname
process
})
I was successful in creating valid new empty require functions after debugging the internals of the Module Class/Function
If you create these 2 following files and run the isolated.js you'll see that it is indeed reloading every time and at the same time the original cache stays intact (although I believe async executions could have unexpected results)
// isolated.js
const Module = require('module')
const vm = require('vm')
const path = require('path')
// this is just to show it wont load it again
const print = require('./print')
print('main file')
const getNewContext = () => {
const mod = new Module()
const filename = path.join(__dirname, `test-filename-${Math.random().toString().substr(-6)}.js`)
const req = Module.createRequire(filename)
req.cache = Object.create(null)
return {
module: mod, // your empty module or a wrapper
exports: mod.exports, // a reference to the module.exports
require: req, // your empty require or a wapper
// require: mod.require, // your empty require or a wapper
__filename: filename,
__dirname: path.dirname(filename),
console,
process
}
}
// print('runningInNewContext')
const jsCode = `
// What?
const log = console.log.bind(null, Date.now())
log({from:'print', cache: require.cache})
const print = require('./print.js')
log(print.toString())
print('evaluated code')
log({from:'print', cache: require.cache})
`
function customRunInNewContext (jsCode, context) {
const { _cache } = Module
Module._cache = Object.create(null)
vm.runInNewContext(jsCode, context)
Module._cache = _cache
}
customRunInNewContext(jsCode, getNewContext())
customRunInNewContext(jsCode, getNewContext())
// print.js
const log = console.log.bind(null, 'PRINT loaded at', new Date().toISOString(), module.parent.filename)
process.stdout.write('\n ====> loading print module\n\n')
module.exports = function print (...args) {
log(...args)
}
When you run
node ./isolated.js
You should see the message ====> loading print module multiple times
One option you have is using 'child_process' fork() API, which spins a new Node process with it's own memory and V8 instance. However this might be just the same, or very similar (at least theoretically) as what you are doing right now.
Keep in mind that spawned Node.js child processes are independent of the parent with exception of the IPC communication channel that is established between the two. Each process has its own memory, with their own V8 instances. Because of the additional resource allocations required, spawning a large number of child Node.js processes is not recommended.
Note that this fork is not a cloning of the entire running process:
Unlike the fork(2) POSIX system call, child_process.fork() does not clone the current process.
Another option which looks promising, is using the Node async_hooks API to emulate executions on separated contexts. Check out node-execution-context, it may work out for your requirements.

Vue cli 3 display info from the package.json

In a vue cli 3 project I want to display a version number in the webpage. The version number lies in the package.json file.
The only reference to this that I found is this link in the vue forum.
However, I can't get the proposed solution to work.
Things I tried
Use webpack.definePlugin as in the linked resource:
vue.config.js
const webpack = require('webpack');
module.exports = {
lintOnSave: true,
configureWebpack: config => {
return {
plugins: [
new webpack.DefinePlugin({
'process.env': {
VERSION: require('./package.json').version,
}
})
]
}
},
}
Then in main.ts I read process.env, but it does not contain VERSION (I tried several variants to this, like generating a PACKAGE_JSON field like in the linked page, and generating plain values like 'foo' instead of reading from package-json). It never worked, it is like the code is being ignored. I guess the process.env is being redefined later by vue webpack stuff.
The process log in main.ts contains, however, all the stuff that process usually contains in a vue-cli project, like the mode and the VUE_APP variables defined in .env files.
Try to write to process right on the configure webpack function,
like:
configureWebpack: config => {
process.VERSION = require('./package.json').version
},
(to be honest I did not have much hope with this, but had to try).
Tried the other solution proposed in the linked page,
like:
// vue.config.js
module.exports = {
chainWebpack: config => {
config.plugin('define').tap( ([options = {}]) => {
return [{
...options, // these are the env variables from your .env file, if any arr defined
VERSION: JSON.stringify(require('./package.json').version)
}]
})
}
}
But this fail silently too.
Use the config.plugin('define') syntax suggested by #Oluwafemi,
like:
chainWebpack: (config) => {
return config.plugin('define').tap(
args => merge(args, [VERSION])
)
},
Where VERSION is a local variable defined as:
const pkgVersion = require('./package.json').version;
const VERSION = {
'process.env': {
VUE_APP_VERSION: JSON.stringify(pkgVersion)
}
}
But this is not working either.
I am re-starting the whole project everytime, so that's not the reason why the process stuff does not show up.
My vue-cli version is 3.0.1.
I am adding my 2 cents, as I found a shorter way and apparently the right way (https://docs.npmjs.com/misc/scripts#packagejson-vars)
Add this in your vue.config.file before the export, not inside:
process.env.VUE_APP_VERSION = process.env.npm_package_version
And voilà!
You can then use it from a component with process.env.VUE_APP_VERSION
TLDR
The following snippet in the vue.config.js file will do the trick, and will allow you to access the version of your app as APPLICATION_VERSION:
module.exports = {
configureWebpack: config => {
return {
plugins: [
new webpack.DefinePlugin({
'APPLICATION_VERSION': JSON.stringify(require('./package.json').version),
})
]
}
},
}
TIP:
Don't even try to add some key to process.env via webpack.definePlugin: it won't work as you probably expect.
 Why my previous efforts did not work
At the end, I solved the issue via webpack.DefinePlugin. The main issue I had is that the original solution I found was using definePlugin to write to a process.env.PACKAGE_JSON variable.
This suggests that definePlugin somehow allows to add variables to process or process.env, which is not the case. Whenever I did log process.env in the console, I didn't find the variables I was trying to push into process.env : so I though the definePlugin tech was not working.
Actually, what webpack.definePlugin does is to check for strings at compile time and change them to its value right on your code. So, if you define an ACME_VERSION variable via:
module.exports = {
lintOnSave: true,
configureWebpack: config => {
return {
plugins: [
new webpack.DefinePlugin({
'ACME_VERSION': 111,
})
]
}
},
}
and then, in main.js you print console.log(ACME_VERSION), you will get 111 properly logged.
Now, however, this is just a string change at compile time. If instead of ACME_VERSION you try to define process.env.VUE_APP_ACME_VERSION...
when you log process.env the VUE_APP_ACME_VERSION key won't show up in the object. However, a raw console.log('process.env.VUE_APP_ACME_VERSION') will yield 111 as expected.
So, basically, original link and the proposed solutions were correct to some degree. However, nothing was really being added to the process object. I was logging proccess.env during my initial tries, so I didn't see anything working.
Now, however, since the process object is not being modified, I strongly suggest anyone trying to load variables to their vue app at compile time not to use it. Is misleading at best.
You can simply import your package.json file and use its variables.
import { version } from "../../package.json";
console.log(version)
If you are using Webpack, you can inject the variable in the following way:
// webpack.config.js
plugins: [
new webpack.DefinePlugin({
VERSION: JSON.stringify(require("package.json").version)
})
]
// any-module.js
console.log("Version: " + VERSION);
https://github.com/webpack/webpack/issues/237
When building the Vue app, environment variables that don't begin with the VUE_APP_ prefix are filtered out. NODE_ENV and BASE_URL environment variables are the exception.
The above information applies when the environment variables are set prior to building the Vue app and not in this situation.
In a situation where environment variables are set during the build, it's important to look at what Vue CLI is doing.
The Vue CLI uses webpack.DefinePlugin to set environment variables using the object returned from the call to resolveClientEnv.
resolveClientEnv returns
{
'process.env': {}
}
This means when configuring your environment variables at build time, you need to come upon a way to merge with the existing one.
You need to perform a deep merge of both arrays, so that value for process.env key is an object containing keys from the resolved client environment and your keys.
chainWebpack key in the default export for vue.config.js is just about one of the ways to get this done.
The arguments passed to initialize the DefinePlugin can be merged with new environment variables that you like to configure using the underlying webpack-chain API. Here is an example:
// vue.config.js
const merge = require('deepmerge');
const pkgVersion = require('./package.json').version;
const VERSION = {
'process.env': {
VERSION: JSON.stringify(pkgVersion)
}
}
module.exports = {
chainWebpack: config =>
config
.plugin('define')
.tap(
args => merge(args, [VERSION])
)
}
Your initial attempt was fine, you were just missing the JSON.stringify part:
const webpack = require('webpack');
module.exports = {
configureWebpack: config => {
return {
plugins: [
new webpack.DefinePlugin({
'process.env': {
VERSION: JSON.stringify(require('./package.json').version),
}
})
]
}
},
}
Edit: although the webpack docs recommend the 'process.env.VERSION' way (yellow panel):
new webpack.DefinePlugin({
'process.env.VERSION': JSON.stringify(require('./package.json').version),
}),
Official solutions tend to be more reliable Modes and Environment Variables | Vue CLI
TIP
You can have computed env vars in your vue.config.js file. They still need to be prefixed with VUE_APP_. This is useful for version info
process.env.VUE_APP_VERSION = require('./package.json').version
module.exports = {
// config
}
I attempted the accepted answer, and had errors. However, in the vue docs, I was able to find an answer similar (but not quite) that of #antoni's answer.
In short, just have the following in vue.config.js:
process.env.VUE_APP_VERSION = require('./package.json').version
module.exports = {
// config
}
Docs 2020-10-27:
You can access env variables in your application code:
process.env.VUE_APP_NOT_SECRET_CODE = require('./package.json').version
During build, process.env.VUE_APP_NOT_SECRET_CODE will be replaced by the corresponding value. In the case of VUE_APP_NOT_SECRET_CODE=some_value, it will be replaced by "some_value".
In addition to VUE_APP_* variables, there are also two special variables that will always be available in your app code:
NODE_ENV - this will be one of "development", "production" or "test" depending on the mode the app is running in.
BASE_URL - this corresponds to the publicPath option in vue.config.js and is the base path your app is deployed at.
The answer for this on the official VueJS forum is like so:
chainWebpack: config => {
config
.plugin('define')
.tap(args => {
let v = JSON.stringify(require('./package.json').version)
args[0]['process.env']['VERSION'] = v
return args
})
}
Description
Add this line to your vue.config.js file
module.exports = {
...
chainWebpack: config => {
config
.plugin('define')
.tap(args => {
let v = JSON.stringify(require('./package.json').version)
args[0]['process.env']['VERSION'] = v
return args
})
}
};
Then you can use this in your vue files like so:
version: function () {
return process.env.VERSION
}
A one liner alternative:
//script tag
let packageJsonInfo = require("../../package.json");
//Then you can for example, get the version no
packageJsonInfo.version

Requirejs: AMD Module not defined

I want to use datepickk.js inside my module: even thought this plugin supports AMD I couldn't load it inside RequireJS:
http://jsfiddle.net/numediaweb/5xbqqr0j/13/
// Config Object
requirejs.config({
// Local Directory
baseUrl: "/js",
// Script Locations
paths: {
// Common Libraries
"jquery": "//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min",
"datepickk": "//crsten.github.io/datepickk/dist/datepickk.min"
}
});
// Check Dependencies
requirejs(['jquery', 'datepickk'], function ($, Datepickk) {
var loadedMods = require.s.contexts._.defined;
console.log(loadedMods);
$('#message').text('Loaded modules => '+JSON.stringify(loadedMods));
return {};
});
If you check the console you will see that jquery is defined and the module not.
Any idea why this happens?
I tried another variation of loading this module:
require.config({
paths: {
'Datepickk': '//crsten.github.io/datepickk/dist/datepickk.min'
},
But then I get this error:
datepickk.js:1146 Uncaught TypeError: Cannot freeze
at Function.freeze (<anonymous>)
at Datepickk (datepickk.js:1146)
at Object.execCb (require.js:1693)
at Module.check (require.js:881)
at Module.enable (require.js:1173)
at Module.init (require.js:786)
at callGetModule (require.js:1200)
at Object.completeLoad (require.js:1587)
at HTMLScriptElement.onScriptLoad (require.js:1714)
Whoever wrote the AMD code for datepickk.js needs to read up on how to write AMD modules. There are two problems:
The module name is hardcoded as Datepickk because the define call is define('Datepickk', Datepickk). The first argument hardcodes the name. This is really a bad thing to do, as the RequireJS documentation is clear that developers should not hardcode names and instead let the optimizer add a name as needed, but here someone was not paying attention.
This explains why your 2nd configuration, the one with Datepickk in paths works, but your first one does not. You must refer to it as Datepickk in your paths configuration. If you want your own code to refer to it as datepickk, you can use a map configuration in addition to paths:
map: {
"*": {
datepickk: "Datepickk"
}
}
Yeah, even if you fix the above, you still get the error you ran into. Looking at the documentation for Datepickk I see that you are use it with do new Datepickk(...). If you do this, then the object to be frozen should be the new object that is assigned to this by the JavaScript virtual machine when the constructor executes. If you look at the code that makes Datepickk available to other code, this is what you see:
if ( typeof define === 'function' && define.amd ) define('Datepickk', Datepickk);
else if ( typeof exports === 'object' ) module.exports = Datepickk;
else window.Datepickk = Datepickk;
The 2nd and 3rd branch export the Datepickk constructor to the world. That's fine. The 1st branch though, which is the one that matters to you, calls define with Datepickk acting as a module factory. When RequireJS executes the define call, it immediately calls Datepickk to build the module. In this situation this is not set to any specific value, so it gets set to the current Window instance (the JavaScript virtual machine does that) and Object.freeze fails. The define call should be:
define(function () {
return Datepickk;
});
(I've also removed the hardcoded module name.) This builds a module that has for value the function Datepickk.

Categories