I'm building a seeding system using Faker over TypeORM throwing the following error on seeding:
🌱 TypeORM Seeding v1.6.1
✔ ORM Config loaded
✔ Factories are imported
✔ Seeders are imported
✔ Database connected
⠹ Executing CreateUsers Seeder
❌ Could not save entity
EntityMetadataNotFound: No metadata for "UsersEntity" was found.
✖ Could not run the seed CreateUsers!
Error: Could not save entity
error Command failed with exit code 1.
I'm using the CLI of the typeorm-seeding npm package in my package.json
"seed": "yarn migration:up && cross-env CLI=true ts-node -r tsconfig-paths/register ./node_modules/typeorm-seeding/dist/cli.js ---root src/core/config -n database-configuration.ts seed",
I have my user.factory.ts using the users.entity.ts
define(UsersEntity, async (faker: typeof Faker) => {
const user = new UsersEntity();
user.firstName = faker.name.firstName();
user.lastName = faker.name.lastName();
user.email = faker.internet.email().toLowerCase();
user.phone = faker.phone.phoneNumber();
user.imageUrl = faker.image.people();
user.password = await CryptoUtils.getHash('password');
user.role = Role.User;
return user;
});
And the user.seeder.ts to run the seeding
export class CreateUsers implements Seeder {
public async run(factory: Factory): Promise<any> {
await factory(UsersEntity)().createMany(10);
}
}
Finally, here is my database configuration used in the CLI to run the seeding
return {
type: 'postgres',
host: process.env.POSTGRES_HOST,
port: +process.env.POSTGRES_PORT,
username: process.env.POSTGRES_USER,
password: process.env.POSTGRES_PASSWORD,
database: process.env.POSTGRES_DB,
entities:
process.env.NODE_ENV === 'test'
? ['src/**/*.entity.ts']
: ['dist/**/*.entity{.ts,.js}'],
synchronize: false,
keepConnectionAlive: true,
migrationsRun: true,
logging: process.env.DATABASE_SHOW_SQL === 'true' || false,
migrations:
process.env.NODE_ENV === 'test'
? ['src/core/database/migrations/**/*.ts']
: ['dist/core/database/migrations/**/*.js'],
seeds: ['src/core/database/seed/seeder/**/*{.ts,.js}'],
factories: ['src/core/database/seed/factory/**/*{.ts,.js}'],
cli: {
migrationsDir: 'src/core/database/migrations',
},
};
I solved this by adding the NODE_ENV check on my factories and seeds files to avoid getting files in dist and src.
The problem was that I am working in development environment. The entities were found in the dist directory instead of factories and seeds who were found in the src instead of the dist folder.
The solution was to apply the process.env.NODE_ENV check on the factories and seeds too:
seeds:
process.env.NODE_ENV === 'test'
? ['src/core/database/seed/seeder/**/*.ts']
: ['dist/core/database/seed/seeder/**/*.js'],
factories:
process.env.NODE_ENV === 'test'
? ['src/core/database/seed/factory/**/*.ts']
: ['dist/core/database/seed/factory/**/*.js'],
Related
In cypress.json file i have the following code
{
"baseUrl": "test",
"ignoreTestFiles": [],
"viewportHeight": 768,
"viewportWidth": 1024,
"video": false,
"env": { "email": "test#email.com", "password": "password" }
}
When i am trying to access it by calling Cypress.env('password') it shows undefined in console log when printing it, what is the issues.
const password: string = Cypress.env('password')
describe("Login to the application", () => {
beforeEach(() => {
cy.visit("/");
});
it.only("user should login successfully", () => {
console.log(Cypress.env('email')). --- undefined
loginPage.login(email, password);
cy.url().should("include", "/wallet");
});
My mistake for not knowing or not checking the location of my cypress.json file, moved it to the top cypress folder and value is shown properly.
In my Projekt (Version 10.xx) the cypress.config.ts must be in the root path not in the cypress folder. You can generate the config with the UI, to get it on the right location:
Settings > Project settings > cypress.config.ts
UPDATE for CYPRESS V10.
Extending #Artjom Prozorov answer,
Now in the newer version the cypress.json naming convention is deprecated.
So, we have to use cypress.config.ts as file name for configuration.
sample of file content given below.
import { defineConfig } from "cypress";
export default defineConfig({
e2e: {
specPattern: "src/**/*.cy.{js,jsx,ts,tsx}",
baseUrl: "http://localhost:3001",
trashAssetsBeforeRuns: false,
viewportWidth:1920,
viewportHeight:1080,
slowTestThreshold: 1000,
// watchForFileChanges : false,
env: {
apiUrl : "http://localhost:3000",
commandDelay: 100,
password: 'here it is'
},
reporter: 'mochawesome',
reporterOptions: {
reportDir: 'cypress/reports',
overwrite: false,
html: true,
json: false
},
setupNodeEvents(on, config) {
config.env.sharedSecret =
process.env.NODE_ENV === 'development' ? 'itsDev' : 'itsLocal'
return config
}
},
component: {
devServer: {
framework: "create-react-app",
bundler: "webpack"
}
}
});
NOTE : this cypress.config.ts must be inside the cypress directory.
In the typeORM documentation a cli parameter can be added to DataSourceOptions according to https://github.com/typeorm/typeorm/blob/master/docs/data-source-options.md. The example I saw on https://typeorm.io/using-cli looks was
{
cli: {
entitiesDir: "src/entity",
subscribersDir: "src/subscriber",
migrationsDir: "src/migration"
}
}
I tried this in my code as follows:
let dataSource = new DataSource(
{
type: 'postgres',
host: 'localhost',
port: 5432,
database: 'website',
username: 'test',
password: 'test',
logging: true,
synchronize: false,
entities: [User, Posts],
cli: {
entitiesDir: "src/entity",
subscribersDir: "src/subscriber",
migrationsDir: "src/migration"
}
})
However I get the following error:
Argument of type '{ type: "postgres"; host: string; port: number; database: string; username: string; password: string; logging: true; synchronize: false; entities: (typeof User | typeof Wallet)[]; cli: { entitiesDir: string; subscribersDir: string; migrationsDir: string; }; }' is not assignable to parameter of type 'DataSourceOptions'.
Object literal may only specify known properties, and 'cli' does not exist in type 'PostgresConnectionOptions'.ts(2345)
I had the same problem. To solve this issue, you can simply remove the cli key from the Datasource options and specify the path when creating a new migration
typeorm migration:create -n UrlMigration -d src/migrations
-d option is used to specify the directory where your migration will be created
"scripts": {
...
"typeorm": "typeorm-ts-node-commonjs -d ormconfig.ts"
}
NB : Replace ormconfig.ts with your datasource filename
refer to TypeORM CLI help
Note: entities, migrations and subscribers can be added to the datasource options in the folowing way:
// ormconfig.ts
export const datasource = new DataSource({
type: "postgres",
host: "localhost",
port: 5432,
database: "database",
username: "username",
password: "password",
entities: [EntityA, EntityB, EntityC],
migrations: [__dirname + "/migrations/*{.js,.ts}"],
subscribers: [],
})
TypeORM and TypeORM CLI not works perfectly after 0.3.0. I had the same problem, so I advice you to downgrade to version 0.2
also, I had a similar issue
typeorm version: 0.3.7
what I did:
I created in src/config/ormconfig-migrations.ts
import { DataSource } from 'typeorm';
const configMigration = new DataSource({
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'mediumclone',
password: 'qwerty',
database: 'mediumclone',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: false,
migrations: [__dirname + '/migrations/**/*{.ts,.js}'],
});
export default configMigration;
in src/comfig/ormconfig.ts
import { DataSourceOptions } from 'typeorm';
const config: DataSourceOptions = {
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'mediumclone',
password: 'qwerty',
database: 'mediumclone',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: false,
migrations: [__dirname + '/migrations/**/*{.ts,.js}'],
};
export default config;
i had separeted, because ormconfig i use for connect db on my app, a ormconfig-migrations.ts - l use to migrations
import { Module } from '#nestjs/common';
import { AppController } from '#app/app.controller';
import { AppService } from '#app/app.service';
import { TagModule } from '#app/tag/tag.module';
import { TypeOrmModule } from '#nestjs/typeorm';
import ormconfig from '#app/config/ormconfig';
#Module({
imports: [TypeOrmModule.forRoot(ormconfig), TagModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
in package.json i added:
"scripts": {
"typeorm": "typeorm-ts-node-commonjs -d src/config/ormconfig-migrations.ts",
"db:drop": "yarn run typeorm schema:drop"
}
in a terminal:
yarn run db:drop
but i have a problem with:
yarn run typeorm migration:generate -n
who did it resolve, please if you resolve this, let know
i hope that with npm other people will not have a problems
i hope it helps to someone
(15/08/2022) Hi!. I update typeorm to 0.3.7, and typeorm is working with DataSource (in the typeorm web page tutorial is called data-source.ts) instead of ormconfig.ts... and i realized that
cli: {
entitiesDir: "src/entity",
subscribersDir: "src/subscriber",
migrationsDir: "src/migration"
}
does not work. But following these steps might help you:
1-- Add these lines in your scripts of your packege.json
"scripts": {
...
"typeorm": "typeorm-ts-node-commonjs",
"typeorm_src": "typeorm-ts-node-commonjs -d src/data-source.ts"
}
"src/data-source.ts" is where you have your database info...
(in case of generate migrations)
In your terminal located in your root directory, you have to write this line
npm run typeorm_src migration:generate src/migrations/nameOfMyMigration
(in case of only create migrations wich is a empty file)
In your terminal located in your root directory, you have to write this line
npm run typeorm migration:create src/migrations/nameOfMyMigration
In both cases the lines are going to generate a file (in src/migrations/nameOfMyMigration) with the migrations to do and with the name {timestamp}-nameOfMyMigration.ts, after the creation of that file, execute this line
npm run typeorm_src migration:run
it is going to make the migrations in your database previously generated in your {timestamp}-nameOfMyMigration.ts.
SOURCE: https://typeorm.io/
I create a NodeJS server and trying to setup environment variables dynamically. But I'm facing a weird problem. I create a env.js file and here is the related code:
const environment = {}
environment.dev = {
port: 3000,
envName: 'dev'
}
environment.prod = {
port: 5000,
envName: 'prod'
}
const currentEnvironment = typeof process.env.NODE_ENV === 'string'
? process.env.NODE_ENV
: 'dev'
console.log('current environment: ', currentEnvironment) // get currentEnvironment: prod
console.log('environment obj: ', environment) // get environment obj correctly
console.log('getting obj from environment obj: ', environment[currentEnvironment]) // But get undefined
const environmentToExport = typeof environment[currentEnvironment] === 'object'
? environment[currentEnvironment]
: environment.dev
module.exports = environmentToExport
I can't figure out why I'm getting undefined in environment[currentEnvironment].
And here is the package.json scripts from where I'm passing environment variable. I'm using windows by the way.
"scripts": {
"dev": "SET NODE_ENV=dev & nodemon index",
"prod": "SET NODE_ENV=prod & nodemon index"
}
Note: I'm using exported port number while creating server. But incase of getting undefined, I'm always exporting dev environment ! Hope you understand my problem.
I wanted to lift my sails aap in production mode.when i run sails lift --prod --verbose, i am getting bunch of errors.
My local.js file looks like this.
/**
* Local environment settings
*
* While you're developing your app, this config file should include
* any settings specifically for your development computer (db passwords, etc.)
* When you're ready to deploy your app in production, you can use this file
* for configuration options on the server where it will be deployed.
*
*
* PLEASE NOTE:
* This file is included in your .gitignore, so if you're using git
* as a version control solution for your Sails app, keep in mind that
* this file won't be committed to your repository!
*
* Good news is, that means you can specify configuration for your local
* machine in this file without inadvertently committing personal information
* (like database passwords) to the repo. Plus, this prevents other members
* of your team from commiting their local configuration changes on top of yours.
*
*
* For more information, check out:
* http://sailsjs.org/#documentation
*/
var config={
development:{
connections: {
mongo: {
adapter: 'sails-mongo',
host: 'localhost',
user: '',
password: '',
database: 'mydata',
schema: true
}
},
mailer:{
hostUrl:'http://localhost:1337/',
emailConfirm:'confirm/',
inviteMoreFriends:'myspace'
},
geoSpatial:{
radiusOfEarth:6375,
radius:3,
maxRecords:20
},
facebook:{
clientID: "CLIENT ID",
clientSecret: "SECRET",
callbackURL: "http://www.EXAMPLE.com:1337/auth/facebook/callback"
}
}
}
module.exports = {
// The `port` setting determines which TCP port your app will be deployed on
// Ports are a transport-layer concept designed to allow many different
// networking applications run at the same time on a single computer.
// More about ports: http://en.wikipedia.org/wiki/Port_(computer_networking)
//
// By default, if it's set, Sails uses the `PORT` environment variable.
// Otherwise it falls back to port 1337.
//
// In production, you'll probably want to change this setting
// to 80 (http://) or 443 (https://) if you have an SSL certificate
port: process.env.PORT || 1337,
// The runtime "environment" of your Sails app is either 'development' or 'production'.
//
// In development, your Sails app will go out of its way to help you
// (for instance you will receive more descriptive error and debugging output)
//
// In production, Sails configures itself (and its dependencies) to optimize performance.
// You should always put your app in production mode before you deploy it to a server-
// This helps ensure that your Sails app remains stable, performant, and scalable.
//
// By default, Sails sets its environment using the `NODE_ENV` environment variable.
// If NODE_ENV is not set, Sails will run in the 'development' environment.
environment: process.env.NODE_ENV || 'development',
development: {
//config is placed as the attributes needed by aws config node module
aws: {
region: 'REGION',
accessKeyId: 'KEY ID',
secretAccessKey: 'SECRET',
cloudFrontCDN: 'EXAMPLE.cloudfront.net'
},
s3: {
Bucket: 'MY_BUCKET',
endpoint: 'ENDPOINT',
imageUrl: 'URL'
},
uploads: {
thumbnails: __dirname + '/../uploads/thumbnails/'
}
},
likeprod: {
//config is placed as the attributes needed by aws config node module
aws: {
region: 'REGION',
accessKeyId: 'KEY ID',
secretAccessKey: 'SECRET',
cloudFrontCDN: 'EXAMPLE.cloudfront.net'
},
s3: {
Bucket: 'MY_BUCKET',
endpoint: 'ENDPOINT',
imageUrl: 'URL'
},
uploads: {
thumbnails: __dirname + '/../uploads/thumbnails/'
}
},
mandrillApiKey:"API_KEY",
twilio:{
accountSid:'SECRET',
authToken:'TOKEN'
},
metaPublic:{
groupBookNumber:'+0123456789'
},
connections:config[process.env.NODE_ENV].connections,
mailer:config[process.env.NODE_ENV].mailer,
geoSpatial:config[process.env.NODE_ENV].geoSpatial,
facebook:config[process.env.NODE_ENV].facebook,
//TODO: refactor the config[environment] as for connections
current: function () {
return sails.config[sails.config.environment]
}
};
when i run sails lift --prod. I am getting this error.
$ sails lift --prod --verbose
info: Starting app...
verbose: Please run `npm install coffee-script` to use coffescript (skipping for now)
verbose: Setting Node environment...
verbose: moduleloader hook loaded successfully.
verbose: Loading app config...
/home/vgulp/Desktop/config/local.js:136
connections:config[process.env.NODE_ENV].connections,
^
TypeError: Cannot read property 'connections' of undefined
at Object.<anonymous> (/home/Desktop/vka/config/local.js:136:45)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Module.require (module.js:364:17)
at require (module.js:380:17)
at /home/Desktop/vka/node_modules/sails/node_modules/include-all/index.js:129:29
at Array.forEach (native)
at requireAll (/home/Desktop/vka/node_modules/sails/node_modules/include-all/index.js:44:9)
at buildDictionary (/home/Desktop/vka/node_modules/sails/node_modules/sails-build-dictionary/index.js:68:14)
at Function.module.exports.aggregate (/home/Desktop/vka/node_modules/sails/node_modules/sails-build-dictionary/index.js:190:9)
at Array.loadOtherConfigFiles [as 0] (/home/Desktop/vka/node_modules/sails/lib/hooks/moduleloader/index.js:102:22)
at /home/Desktop/vka/node_modules/sails/node_modules/async/lib/async.js:459:38
at Array.forEach (native)
at _each (/home/Desktop/vka/node_modules/sails/node_modules/async/lib/async.js:32:24)
at Object.async.auto (/home/Desktop/vka/node_modules/sails/node_modules/async/lib/async.js:430:9)
Can anyone suggest a solution.
[ Edited: the following answer was based on the original question which was completely changed by the author ]
Your sails app need to lift in production mode or you need to specify the port to be used in your config files.
Production mode runs your express server on port 80.
Is your AWS instance setup to lift the app in production mode?
http://sailsjs.org/documentation/anatomy/my-app/config/env/production-js
You don 't have connection specified for production in local.js (As you are running from your Desktop)
As the error rightly says,
connections:config[process.env.NODE_ENV].connections,
^
TypeError: Cannot read property 'connections' of undefined
process.env.NODE_ENV is production when running in --prod
var config = {
development: {
connections: {
mongo: {
adapter: 'sails-mongo',
host: 'localhost',
user: '',
password: '',
database: 'mydata',
schema: true
}
},
mailer: {
hostUrl: 'http://localhost:1337/',
emailConfirm: 'confirm/',
inviteMoreFriends: 'myspace'
},
geoSpatial: {
radiusOfEarth: 6375,
radius: 3,
maxRecords: 20
},
facebook: {
clientID: "CLIENT ID",
clientSecret: "SECRET",
callbackURL: "http://www.EXAMPLE.com:1337/auth/facebook/callback"
}
},
production: {
connections: {
mongo: {
adapter: 'sails-mongo',
host: 'localhost',
user: '',
password: '',
database: 'mydata',
schema: true
}
},
mailer: {
hostUrl: 'http://localhost:1337/',
emailConfirm: 'confirm/',
inviteMoreFriends: 'myspace'
},
geoSpatial: {
radiusOfEarth: 6375,
radius: 3,
maxRecords: 20
},
facebook: {
clientID: "CLIENT ID",
clientSecret: "SECRET",
callbackURL: "http://www.EXAMPLE.com:1337/auth/facebook/callback"
}
}
}
I am using the npm modules grunt env and load-grunt-config in my project. grunt env handles environment variables for you, while load-grunt-config handles, well, loads the grunt configuration for you. You can put your tasks into other files, then load-grunt-config will bundle them up and have grunt load & consume them for you. You can also make an aliases.js file, with tasks you want to combine together into one task, running one after another. It's similar to the grunt.registerTask task in the original Gruntfile.js. I put all my grunt tasks inside a separate grunt/ folder under the root folder with the main Gruntfile, with no extra subfolders, as suggested by the load-grunt-config README.md on Github. Here is my slimmed-down Gruntfile:
module.exports = function(grunt) {
'use strict';
require('time-grunt')(grunt);
// function & property declarations
grunt.initConfig({
pkg: grunt.file.readJSON('package.json')
});
require('load-grunt-config')(grunt, {
init: true,
loadGruntConfig: {
scope: 'devDependencies',
pattern: ['grunt-*', 'time-grunt']
}
});
};
In theory, setting all these files up the correct way for load-grunt-config to load should be exactly the same as just having a Gruntfile.js. However, I seem to have run into a little snag. It seems the environment variables set under the env task do not get set for the subsequent grunt tasks, but are set by the time node processes its tasks, in this case an express server.
grunt env task:
module.exports = {
// environment variable values for developers
// creating/maintaining site
dev: {
options: {
add: {
NODE_ENV: 'dev',
MONGO_PORT: 27017,
SERVER_PORT: 3000
}
}
}
};
grunt-shell-spawn task:
// shell command tasks
module.exports = {
// starts up MongoDB server/daemon
mongod: {
command: 'mongod --bind_ip konneka.org --port ' + (process.env.MONGO_PORT || 27017) + ' --dbpath C:/MongoDB/data/db --ipv6',
options: {
async: true, // makes this command asynchronous
stdout: false, // does not print to the console
stderr: true, // prints errors to the console
failOnError: true, // fails this task when it encounters errors
execOptions: {
cwd: '.'
}
}
}
};
grunt express task:
module.exports = {
// default options
options: {
hostname: '127.0.0.1', // allow connections from localhost
port: (process.env.SERVER_PORT || 3000), // default port
},
prod: {
options: {
livereload: true, // automatically reload server when express pages change
// serverreload: true, // run forever-running server (do not close when finished)
server: path.resolve(__dirname, '../backend/page.js'), // express server file
bases: 'dist/' // watch files in app folder for changes
}
}
};
aliases.js file (grunt-load-config's way of combining tasks so they run one after the other):
module.exports = {
// starts forever-running server with "production" environment
server: ['env:prod', 'shell:mongod', 'express:prod', 'express-keepalive']
};
part of backend/env/prod.js (environment-specific Express configuration, loaded if NODE_ENV is set to "prod", modeled after MEAN.JS):
'use strict';
module.exports = {
port: process.env.SERVER_PORT || 3001,
dbUrl: process.env.MONGOHQ_URL || process.env.MONGOLAB_URI || 'mongodb://konneka.org:' + (process.env.MONGO_PORT || 27018) + '/mean'
};
part of backend/env/dev.js (environment-specific Express configuration for dev environment, loaded if the `NODE_ENV variable is not set or is set to "dev"):
module.exports = {
port: process.env.SERVER_PORT || 3000,
dbUrl: 'mongodb://konneka.org:' + (process.env.MONGO_PORT || 27017) + '/mean-dev'
};
part of backend/page.js (my Express configuration page, also modeled after MEAN.JS):
'use strict';
var session = require('express-session');
var mongoStore = require('connect-mongo')(session);
var express = require('express');
var server = express();
...
// create the database object
var monServer = mongoose.connect(environ.dbUrl);
// create a client-server session, using a MongoDB collection/table to store its info
server.use(session({
resave: true,
saveUninitialized: true,
secret: environ.sessionSecret,
store: new mongoStore({
db: monServer.connections[0].db, // specify the database these sessions will be saved into
auto_reconnect: true
})
}));
...
// listen on port related to environment variable
server.listen(process.env.SERVER_PORT || 3000);
module.exports = server;
When I run grunt server, I get:
$ cd /c/repos/konneka/ && grunt server
Running "env:prod" (env) task
Running "shell:mongod" (shell) task
Running "express:prod" (express) task
Running "express-server:prod" (express-server) task
Web server started on port:3000, hostname: 127.0.0.1 [pid: 3996]
Running "express-keepalive" task
Fatal error: failed to connect to [konneka.org:27018]
Execution Time (2014-08-15 18:05:31 UTC)
loading tasks 38.3s █████████████████████████████████ 79%
express-server:prod 8.7s ████████ 18%
express-keepalive 1.2s ██ 2%
Total 48.3s
Now, I can't seem to get the database connected in the first place, but ignore that for now. Notice that the server is started on port 3000, meaning that during execution of the grunt express:prod task, SERVER_PORT is not set so the port gets set to 3000. There are numerous other examples like this, where an environment variable is not set so my app uses the default. However, notice that session tries to connect to the database on port 27018 (and fails), so MONGO_PORT does get set eventually.
If I had just tried the grunt server task, I could chalk it up to load-grunt-config running the tasks in parallel instead of one after the other or some other error, but even when I try the tasks one-by-one, such as running grunt env:prod shell:mongod express-server:prod express-keepalive, I get similar (incorrect) results, so either grunt or grunt env run the tasks in parallel, as well, or something else is going on.
What's going on here? Why are the environment variables not set correctly for later grunt tasks? When are they eventually set, and why then rather than some other time? How can I make them get set for grunt tasks themselves rather than after, assuming there even is a way?
The solution is rather obvious once you figure it out, so let's start at the beginning:
The problem
You're using load-grunt-config to load a set of modules (objects that define tasks) and combine them into one module (object) and pass it along to Grunt. To better understand what load-grunt-config is doing, take a moment to read through the source (it's just three files). So, instead of writing:
// filename: Gruntfile.js
grunt.initConfig({
foo: {
a: {
options: {},
}
},
bar: {
b: {
options: {},
}
}
});
You can write this:
// filename: grunt/foo.js
module.exports = {
a: {
options: {},
}
}
// filename: grunt/bar.js
module.exports = {
b: {
options: {},
}
}
// filename: Gruntfile.js
require('load-grunt-config')(grunt);
Basically, this way you can split up a Grunt configuration into multiple files and have it be more "maintainable". But what you'll need to realize is that these two approaches are semantically equivalent. That is, you can expect them to behave the same way.
Thus, when you write the following*:
(* I've reduced the problem in an attempt to make this answer a bit more general and to reduce noise. I've excluded things like loading the tasks and extraneous option passing, but the error should still be the same. Also note that I've changed the values of the environment variables because the default was the same as what was being set.)
// filename: grunt/env.js
module.exports = {
dev: {
options: {
add: {
// These values are different for demo purposes
NODE_ENV: 'dev',
MONGO_PORT: 'dev_mongo_port',
SERVER_PORT: 'dev_server_port'
}
}
}
};
// filename: grunt/shell.js
module.exports = {
mongod: {
command: 'mongod --port ' + (process.env.MONGO_PORT || 27017)
}
};
// filename: grunt/aliases.js
module.exports = {
server: ['env:prod', 'shell:mongod']
};
// filename: Gruntfile.js
module.exports = function (grunt) {
require('load-grunt-config')(grunt);
};
You can consider the above the same as below:
module.exports = function (grunt) {
grunt.initConfig({
env: {
dev: {
options: {
add: {
NODE_ENV: 'dev',
MONGO_PORT: 'dev_mongo_port',
SERVER_PORT: 'dev_server_port'
}
}
}
},
shell: {
mongod: {
command: 'mongod --port ' + (process.env.MONGO_PORT || 27017)
}
}
});
grunt.registerTask('server', ['env:dev', 'shell:mongod']);
};
Now do you see the problem? What command do you expect shell:mongod to run? The correct answer is:
mongod --port 27017
Where what you want to be executed is:
mongo --port dev_mongo_port
The problem is that when (process.env.MONGO_PORT || 27017) is evaluated the environment variables have not yet been set (i.e. before the env:dev task has been run).
A solution
Well let's look at a working Grunt configuration before splitting it across multiple files:
module.exports = function (grunt) {
grunt.initConfig({
env: {
dev: {
options: {
add: {
NODE_ENV: 'dev',
MONGO_PORT: 'dev_mongo_port',
SERVER_PORT: 'dev_server_port'
}
}
}
},
shell: {
mongod: {
command: 'mongod --port ${MONGO_PORT:-27017}'
}
}
});
grunt.registerTask('server', ['env:dev', 'shell:mongod']);
};
Now when you run shell:mongod, the command will contain ${MONGO_PORT:-27017} and Bash (or just sh) will look for the environment variable you would have set in the task before it (i.e. env:dev).
Okay, that's all well and good for the shell:mongod task, but what about the other tasks, Express for example?
You'll need to move away from environment variables (unless you want to set them up before invoking Grunt. Why? Take this Grunt configuration for example:
module.exports = function (grunt) {
grunt.initConfig({
env: {
dev: {
options: {
add: {
NODE_ENV: 'dev',
MONGO_PORT: 'dev_mongo_port',
SERVER_PORT: 'dev_server_port'
}
}
}
},
express: {
options: {
hostname: '127.0.0.1'
port: (process.env.SERVER_PORT || 3000)
},
prod: {
options: {
livereload: true
server: path.resolve(__dirname, '../backend/page.js'),
bases: 'dist/'
}
}
}
});
grunt.registerTask('server', ['env:dev', 'express:prod']);
};
What port will the express:prod task configuration contain? 3000. What you need is for it to reference the value you've defined in the above task. How you do this is up to you. You could:
Separate the env configuration and reference its values
module.exports = function (grunt) {
grunt.config('env', {
dev: {
options: {
add: {
NODE_ENV: 'dev',
MONGO_PORT: 'dev_mongo_port',
SERVER_PORT: 'dev_server_port'
}
}
}
});
grunt.config('express', {
options: {
hostname: '127.0.0.1'
port: '<%= env.dev.options.add.SERVER_PORT %>'
}
});
grunt.registerTask('server', ['env:dev', 'express:prod']);
};
But you'll notice that the semantics of the env task don't hold up here due to it no longer representing a task's configuration. You could use an object of your own design:
module.exports = function (grunt) {
grunt.config('env', {
dev: {
NODE_ENV: 'dev',
MONGO_PORT: 'dev_mongo_port',
SERVER_PORT: 'dev_server_port'
}
});
grunt.config('express', {
options: {
hostname: '127.0.0.1'
port: '<%= env.dev.SERVER_PORT %>'
}
});
grunt.registerTask('server', ['env:dev', 'express:prod']);
};
Pass grunt an argument to specify what config it should use
Have multiple configuration files (e.g. Gruntfile.js.dev and Gruntfile.js.prod) and rename them as needed
Read a development configuration file (e.g. grunt.file.readJSON('config.development.json')) if it exists and fall back to a production configuration file if it doesn't exist
Some better way not listed here
But all of the above should achieve the same end result.
This seems to be the essence of what you are trying to do, and it works for me. The important part was what I mentioned in my comment -- chaining the environment task before running the other tasks.
Gruntfile.js
module.exports = function(grunt) {
// Do grunt-related things in here
grunt.loadNpmTasks('grunt-env');
grunt.initConfig({
env: {
dev: {
PROD : 'http://production.server'
}
}
});
grunt.registerTask('printEnv', 'prints a message with an env var', function() { console.log('Env var in subsequent grunt task: ' + process.env.PROD) } );
grunt.registerTask('prod', ['env:dev', 'printEnv']);
};
Output of grunt prod
Running "env:dev" (env) task
Running "printEnv" task
Env var in subsequent grunt task: http://production.server
Done, without errors.