Coder Social home page Coder Social logo

sequelize-markup's Introduction

sequelize-markup

This is a Babel plugin that transpiles indent aware markup into a Sequelize configuration and model definition.

Features:

  • Markup syntax for declaring models
  • Declare models in a single file or in multiple files
  • Connection configuration stored in a file or in code
  • Multiple database environments
  • Conditional declarations

Model in single file, with configuration in code:

src/index.js:
// configure sequelize
var db = SQLZINIT>
  // sequelize configuration options
  (config)
    // "development" db environment
    (development(dialect="sqlite"))
      (storage="./db.development.sqlite")
    // "test" db environment
    (test)
      (dialect="sqlite")
  // execution environment
  (environment="development")

// configure models
SQLZ>
  (User)
    (name(type=DataTypes.STRING(60)))
    (...associations)
      (Tasks=hasMany.Task)

  (Task)
    (title(type=DataTypes.STRING(255)))

// configure associations
SQLZINIT(db)

// create database and insert a record
db.sequelize.sync({ force: true }).then(() => {
  db.User.findOrCreate({ 
    where: { name: 'testuser' }, 
    defaults: { other: 'ok' } } )
    .spread( (user, wasCreated) => {
      console.log(user.get( { plain: true } ));
    });
});
Transpiles to:
src/index.js:
var db = {};

var Sequelize = require("sequelize"),
  DataTypes = Sequelize.DataTypes,
  env = "development",
  cfg = {
    development: {
      dialect: "sqlite",
      storage: "./db.development.sqlite"
    },
    test: {
      dialect: "sqlite"
    }
  }[env],
  sequelize = new Sequelize(cfg);

db.sequelize = sequelize;
db.Sequelize = Sequelize;
const User = sequelize.define("User", {
  name: {
    type: DataTypes.STRING(60)
  }
});

User.associate = sequelize => {
  User.Tasks = User.hasMany(sequelize.models.Task, {});
};

const Task = sequelize.define("Task", {
  title: {
    type: DataTypes.STRING(255)
  }
});

for (let mdl in sequelize.models) {
  let model = sequelize.models[mdl];
  db[mdl] = model;
  if (model.associate) model.associate(sequelize);
}

db.sequelize.sync({ force: true }).then(() => {
  db.User.findOrCreate({
    where: { name: 'testuser' },
    defaults: { other: 'ok' } }).spread((user, wasCreated) => {
      console.log(user.get({ plain: true }));
    });
});

Models in multiple files with configuration in external file:

src/db/index.js:
var path = require("path");

// configure sequelize and import models
var db = SQLZINIT>
  // path to configuration file
  (config=path.join(__dirname, "..", "..", "config", "config.json"))
  // execution environment
  (environment=process.env.NODE_ENV || "development")
  // optional database URL
  (url=process.env.DATABASE_URL)
  // model files glob
  (models=path.join(__dirname, "**/!(index).js"))

// configure associations
SQLZINIT(db);

module.exports = db;
src/db/user.js:
export default function(sequelize, DataTypes) {
  SQLZ>
    (User)
      (name(type=DataTypes.STRING(60)))
      (...associations)
        (Tasks=hasMany.Task)
}
src/db/task.js:
export default function(sequelize, DataTypes) {
  SQLZ>
    (Task)
      (title(type=DataTypes.STRING(255)))
}
src/index.js:
var path = require('path');
var db = require(path.join(__dirname, 'db'));

db.sequelize.sync({ force: true }).then(() => {
  db.User.findOrCreate({ 
    where: { name: 'testuser' }, 
    defaults: { other: 'ok' } } )
    .spread( (user, wasCreated) => {
      console.log(user.get( { plain: true } ));
    });
});
config/config.json
{
  "development": {
    "dialect": "sqlite",
    "storage": "./db.development.sqlite"
  },
  "test": {
    "dialect": "sqlite",
    "storage": ":memory:"
  },
  "production": {
    "username": null,
    "password": null,
    "database": "database_production",
    "host": "127.0.0.1",
    "dialect": "mysql",
    "logging": false
  }
}
Transpiles to:
src/db/index.js:
var path = require("path");

var db = {};

var Sequelize = require("sequelize"),
  DataTypes = Sequelize.DataTypes,
  env = process.env.NODE_ENV || "development",
  dbUrl = process.env.DATABASE_URL,
  cfgFile = path.join(__dirname, "..", "..", "config", "config.json"),
  cfg = require(cfgFile)[env],
  sequelize = dbUrl ? new Sequelize(dbUrl, cfg) : new Sequelize(cfg),
  glob = require("glob"),
  modelGlob = path.join(__dirname, "**/!(index).js"),
  files = glob.sync(modelGlob);

for (let i = 0; i < files.length; i++) sequelize.import(files[i]);

db.sequelize = sequelize;
db.Sequelize = Sequelize;

for (let mdl in sequelize.models) {
  let model = sequelize.models[mdl];
  db[mdl] = model;
  if (model.associate) model.associate(sequelize);
}

module.exports = db;
src/db/user.js:
export default function (sequelize, DataTypes) {
  const User = sequelize.define("User", {
    name: {
      type: DataTypes.STRING(60)
    }
  });

  User.associate = sequelize => {
    User.Tasks = User.hasMany(sequelize.models.Task, {});
  };
}
src/db/task.js:
export default function (sequelize, DataTypes) {
  const Task = sequelize.define("Task", {
    title: {
      type: DataTypes.STRING(255)
    }
  });
}

Syntax

SQLZINIT>

The SQLZINIT> directive is used to declare a Sequelize configuration. Indented child elements that follow this directive are used to create the configuration.

Example:
var db = SQLZINIT>
  // path to configuration file
  (config=path.join(__dirname, "..", "..", "config", "config.json"))
  // execution environment
  (environment=process.env.NODE_ENV || "development")
  // optional database URL
  (url=process.env.DATABASE_URL)
  // optional model files glob
  (models=path.join(__dirname, "**/!(index).js"))
Transpiles to:
var db = {}; 
var Sequelize = require("sequelize"),
  DataTypes = Sequelize.DataTypes,
  env = process.env.NODE_ENV || "development",
  dbUrl = process.env.DATABASE_URL,
  cfgFile = path.join(__dirname, "..", "..", "config", "config.json"),
  cfg = require(cfgFile)[env],
  sequelize = dbUrl ? new Sequelize(dbUrl, cfg) : new Sequelize(cfg),
  glob = require("glob"),
  modelGlob = path.join(__dirname, "**/!(index).js"),
  files = glob.sync(modelGlob);

for (let i = 0; i < files.length; i++) sequelize.import(files[i]);

db.sequelize = sequelize;
db.Sequelize = Sequelize;

(environment="name")

The (environment="name") element specifies which database environment Sequelize should use. The value is used to select which configuration Sequelize uses.

  (environment=process.env.NODE_ENV || "development")

(config="config.json")

The (config="config.json") element specifies a JSON file to load as the Sequelize configuration. Each root key specifies a database environment.

Sample config.json file:

{
  "development": {
    "dialect": "sqlite",
    "storage": "./db.development.sqlite"
  },
  "test": {
    "dialect": "sqlite",
    "storage": ":memory:"
  },
  "production": {
    "username": "user",
    "password": "pass",
    "database": "database_production",
    "host": "127.0.0.1",
    "dialect": "mysql",
    "logging": false
  }
}

(config)

To specify the Sequelize configuration inside a source file, use the (config) element:

Example:
var db = SQLZINIT>
  // path to configuration file
  (config)
      (development)
          (dialect="sqlite")
          (storage="./db.development.sqlite")
        (test(dialect="sqlite", storage=":memory:"))
            (logging=false)
  // execution environment
  (environment=process.env.NODE_ENV || "development")
Transpiles to:
var db = {};

var Sequelize = require("sequelize"),
  DataTypes = Sequelize.DataTypes,
  env = process.env.NODE_ENV || "development",
  cfg = {
    development: {
      dialect: "sqlite",
      storage: "./db.development.sqlite"
    },
    test: {
      dialect: "sqlite",
      storage: ":memory:",
      logging: false
    }
  }[env],
  sequelize = new Sequelize(cfg);

db.sequelize = sequelize;
db.Sequelize = Sequelize;

Note that you can specify keys and values as element attributes or child elements.

(url="database URL")

Use the optional (url="database URL") element to declare a URL to use when connecting to the database. This allows for an environment variable to decide which database connection is used.

(url=process.env.DATABASE_URL)

(models="glob")

The optional (models="glob") element uses the glob package to import models contained in files that recursively match the wildcard pattern.

// model files glob (all but index.js)
(models=path.join(__dirname, "**/!(index).js"))

SQLZINIT(db)

After models have been defined and/or imported, use SQLZINIT(db) to initialize all model assocations.

Example:
SQLZINIT(db) // use the variable that SQLZINIT> assigns to.
Transpiles to:
for (var mdl in sequelize.models) {
  var model = sequelize.models[mdl];
  db[mdl] = model;
  if (model.associate) model.associate(sequelize);
}

SQLZ>

One or more tables are defined using SQLZ>. This will transpile the child elements that follow into Sequelize calls.

Root nodes map to database tables. Child nodes map to either columns or table options. Table options are declared using one of the following elements: (...options), (...name), (...columns), (...getters), (...setters), (...validate), (...indexes), (...associations)

Example:
SQLZ>
    (Table1)
      (Column1(type=DataTypes.STRING))
      (Column2(type=DataTypes.STRING))
      (...columns) // can also specify columns here
        (AnotherColumn1(type=DataTypes.STRING))
      (...name)
        (singular='tableone')
        (plural='tableones')
    (Table2) // multiple tables are allowed
      (test(type=DataTypes.STRING))
Transpiles to:
const Table1 = sequelize.define('Table1', {
  Column1: {
    type: DataTypes.STRING
  },
  Column2: {
    type: DataTypes.STRING
  },
  AnotherColumn1: {
    type: DataTypes.STRING
  }
}, {
  name: {
    singular: 'tableone',
    plural: 'tableones'
  }
});
const Table2 = sequelize.define('Table2', {
  test: {
    type: DataTypes.STRING
  }
});

(...options)

The (...options) element is used to specify table options. Table options specified using dot notation are set to true. Table options can also be specified as an attribute of the table element.

Example:
SQLZ>
  (User.timestamps.createdAt.updatedAt.deletedAt(comment='The user table'))
    (column1.unique(type=DataTypes.BOOLEAN))
      (onUpdate='CASCADE')
    (...options)
      (defaultScope)
        (where(active=true))
      (omitNull=false)
      (paranoid=false)
      (underscored=false)
      (underscoredAll=false)
      (freezeTableName=false)
      (tableName='users')
      (schema='public')
      (engine='MYISAM')
      (initialAutoIncrement='1')
Transpiles to:
const User = sequelize.define('User', {
  column1: {
    unique: true,
    type: DataTypes.BOOLEAN,
    onUpdate: 'CASCADE'
  }
}, {
  timestamps: true,
  createdAt: true,
  updatedAt: true,
  deletedAt: true,
  comment: 'The user table',
  defaultScope: {
    where: {
      active: true
    }
  },
  omitNull: false,
  paranoid: false,
  underscored: false,
  underscoredAll: false,
  freezeTableName: false,
  tableName: 'users',
  schema: 'public',
  engine: 'MYISAM',
  initialAutoIncrement: '1'
});

(...name)

The (...name) element is used to explicitly configure table names.

Example:
SQLZ>
  (User)
    (name(type=DataTypes.STRING))
    (...name)
      (singular='loginuser')
      (plural='loginusers')
Transpiles to:
const User = sequelize.define('User', {
  name: {
    type: DataTypes.STRING
  }
}, {
  name: {
    singular: 'loginuser',
    plural: 'loginusers'
  }
});

(...columns)

The (...columns) element allows columns to be declared. This is mainly for organization as columns can also be declared as a direct child element of a table.

Example:
SQLZ>
  (User)
    (...columns)
      (name.unique(type=DataTypes.STRING(60)))
        (get=() => { return this.getDataValue('name'); })
        (set=(val) => { this.setDataValue('name', val); })
        (validate.isAlphanumeric)
          (notNull(msg='name can\'t be null'))
          (isEven=(val) => { throw new Error('Bad validation'); })
          (isNotNull=true)
Transpiles to:
const User = sequelize.define('User', {
  name: {
    unique: true,
    type: DataTypes.STRING(60),
    function get() {
      return this.getDataValue('id');
    },
    function set(val) {
      this.setDataValue('id', val);
    },
    validate: {
      isAlphanumeric: true,
      isInt: true,
      notNull: {
        msg: 'name can\'t be null'
      },
      function isEven(val) {
        throw new Error('Bad validation');
      },
      isNotNull: true
    }
  }
});

(...getters)

The (...getters) element is where custom getters are declared.

Example:
SQLZ>
  (User)
    (name(type=DataTypes.STRING(60)))
    (...getters)
      (getTwoName=() => { 
        return this.getDataValue('name') + " " + this.getDataValue('name'); 
      })
Transpiles to:
const User = sequelize.define('User', {
  name: {
    type: DataTypes.STRING(60)
  }
}, {
  getters: {
    function getTwoName() {
      return 
        this.getDataValue('name') + " " +
        this.getDataValue('name');
    }

  }
});

(...setters)

The (...setters) element is where custom setters are declared.

Example:
SQLZ>
  (User)
    (name(type=DataTypes.STRING(60)))
    (...setters)
      (setFunName=(val) => { 
        this.setDataValue('name', 'Fun' + val);
      })
Transpiles to:
const User = sequelize.define('User', {
  name: {
    type: DataTypes.STRING(60)
  }
}, {
  setters: {
    function setFunName(val) {
      this.setDataValue('name', 'Fun' + val);
    }
  }
});

(...validate)

The (...validate) element is where custom validations are declared.

Example:
SQLZ>
  (User)
    (name(type=DataTypes.STRING(60)))
    (...validate)
      (namesAreOk=() => { 
        if (this.name == "Sam")
          throw new Error("Invalid name"); 
      })
Transpiles to:
const User = sequelize.define("User", {
  name: {
    type: DataTypes.STRING(60)
  }
}, {
  validate: {
    function namesAreOk() {
      if (this.name == "Sam")
          throw new Error("Invalid name");
    }
  }
});

(...indexes)

The (...indexes) element is where custom indexes are declared.

Example:
SQLZ>
  (User)
    (name(type=DataTypes.STRING(60)))
    (status(type=DataType.STRING(60)))
    (...indexes)
      (user.unique(fields=['name']))
      (user_status)
        (unique=false)
        (fields=['status'])
        (where)
          (status='public')
Transpiles to:
const User = sequelize.define('User', {
  name: {
    type: DataTypes.STRING(60)
  },
  status: {
    type: DataType.STRING(60)
  }
}, {
  indexes: {
    user: {
      unique: true,
      fields: ['name']
    },
    user_status: {
      unique: false,
      fields: ['status'],
      where: {
        status: 'public'
      }
    }
  }
});

(...associations)

The (...associations) element is where table associations are declared. Note that these associatations need to be initialized by calling table.associate() unless you use SQLZINIT(db).

Example:
SQLZ>
  (User)
    (name(type=DataTypes.STRING(60)))
    (...associations)
      (Orgs=belongsTo.Organization)
      (Projects=belongsToMany.Project(through='UserProject'))
        (constraints=false)
Transpiles to:
const User = sequelize.define('User', {
  name: {
    type: DataTypes.STRING(60)
  }
});

User.associate = sequelize => {
  User.Orgs = User.belongsTo(sequelize.models.Organization, {});
  User.Projects = User.belongsToMany(sequelize.models.Project, {
    through: 'UserProject',
    constraints: false
  });
};

(...scopes)

The (...scopes) element is where custom query scoping is declared.

Example:
SQLZ>
  (User)
    (name(type=DataTypes.STRING(60)))
    (active(type=DataTypes.BOOLEAN))
    (...scopes)
      (activeUsers)
        (where(active=true))
Transpiles to:
const User = sequelize.define("User", {
  name: {
    type: DataTypes.STRING(60)
  },
  active: {
    type: DataTypes.BOOLEAN
  }
}, {
  scopes: {
    activeUsers: {
      where: {
        active: true
      }
    }
  }
});

(...hooks)

The (...hooks) element is where custom hooks are declared.

Example:
SQLZ>
  (User)
    (name(type=DataTypes.STRING(60)))
    (...hooks)
      (beforeValidate=(instance, options) => { })
      (afterValidate=(instance, options) => { })
Transpiles to:
const User = sequelize.define("User", {
  name: {
    type: DataTypes.STRING(60)
  }
}, {
  hooks: {
    beforeValidate: (instance, options) => {},
    afterValidate: (instance, options) => {}
  }
});

Conditional Declarations

The ($if), ($elseif), and ($else) elements conditionally declare model elements.

Example:
SQLZ>
  (User)
    ($if (firstAndLast))
      (firstName(type=DataTypes.STRING(60)))
      ($if (middle))
        (middleName(type=DataTypes.STRING(60))
      (lastName(type=DataTypes.STRING(60)))
    ($else)
      (name(type=DataTypes.STRING(60)))
    (...options)
      ($if (mysql))
        (engine="MYISAM")
Transpiles to:
const User = sequelize.define("User", Object.assign({}, firstAndLast ? Object.assign({
  firstName: {
    type: DataTypes.STRING(60)
  },
  lastName: {
    type: DataTypes.STRING(60)
  }
}, middle ? {
  middleName: {
    type: DataTypes.STRING(60)
  }
} : undefined) : {
  name: {
    type: DataTypes.STRING(60)
  }
}), Object.assign({}, mysql ? {
  engine: "MYISAM"
} : undefined));

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.