Skip to content

Commit

Permalink
fix: Add support for 'scalar' keyword
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolasdao committed Nov 27, 2017
1 parent 5ac1ec0 commit 351e2e5
Show file tree
Hide file tree
Showing 6 changed files with 234 additions and 63 deletions.
Binary file removed .DS_Store
Binary file not shown.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
node_modules/*
node_modules/*
*.DS_Store
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"test": "mocha ./test/node/*.js",
"test:dev": "MOCHA_ENV=dev mocha ./test/node/*.js",
"release": "standard-version",
"eslint": "eslint graphqls2s.js src/ test/",
"eslint": "eslint graphqls2s.js src/ test/ --fix",
"build": "WEBPACK_ENV=build webpack",
"dev": "WEBPACK_ENV=dev webpack"
},
Expand Down
167 changes: 106 additions & 61 deletions src/graphqls2s.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,19 @@ const propertyParamsRegEx = /\((.*?)\)/
const carrReturnEsc = '_cr_'
const tabEsc = '_t_'

let s = null
const escapeGraphQlSchemaPlus = (sch, cr, t) => s || (() => { s = escapeGraphQlSchema(sch, cr, t); return s })()
let _s = {}
const escapeGraphQlSchemaPlus = (sch, cr, t) => {
if (!sch)
return sch

if (!_s[sch])
_s[sch] = escapeGraphQlSchema(sch, cr, t)

return _s[sch]
}

/**
* Gets a first rough break down of the string schema
* Gets a first rough breakdown of the string schema
* @param {String} sch Original GraphQl Schema
* @return {Array} Using regex, the interfaces, types, inputs, enums and abstracts entities are isolated
* e.g. [{
Expand All @@ -39,27 +47,49 @@ const escapeGraphQlSchemaPlus = (sch, cr, t) => s || (() => { s = escapeGraphQlS
* extend: false
* }]
*/
const typeRegex = /(extend type|type)\s(.*?){(.*?)_cr_([^#]*?)}/mg
const inputRegex = /(extend input|input)\s(.*?){(.*?)_cr_([^#]*?)}/mg
const enumRegex = /enum\s(.*?){(.*?)_cr_([^#]*?)}/mg
const interfaceRegex = /(extend interface|interface)\s(.*?){(.*?)_cr_([^#]*?)}/mg
const abstractRegex = /(extend abstract|abstract)\s(.*?){(.*?)_cr_([^#]*?)}/mg
const getSchemaBits = (sch='') => _.flatten(_.toArray(_([typeRegex, inputRegex, enumRegex, interfaceRegex, abstractRegex])
.map(rx => _.toArray(_(escapeGraphQlSchemaPlus(sch, carrReturnEsc, tabEsc).match(rx)).map(str => {
const blockMatch = str.match(/{(.*?)_cr_([^#]*?)}/)
if (!blockMatch) {
const msg = 'Schema error: Missing block'
log(msg)
throw new Error(msg)
}
const typeRegex = { regex: /(extend type|type)\s(.*?){(.*?)_cr_([^#]*?)}/mg, type: 'type' }
const inputRegex = { regex: /(extend input|input)\s(.*?){(.*?)_cr_([^#]*?)}/mg, type: 'input' }
const enumRegex = { regex: /enum\s(.*?){(.*?)_cr_([^#]*?)}/mg, type: 'enum' }
const interfaceRegex = { regex: /(extend interface|interface)\s(.*?){(.*?)_cr_([^#]*?)}/mg, type: 'interface' }
const abstractRegex = { regex: /(extend abstract|abstract)\s(.*?){(.*?)_cr_([^#]*?)}/mg, type: 'abstract' }
const scalarRegex = { regex: /(.{1}|.{0})scalar\s(.*?)([^\s]*?)(?![a-zA-Z0-9])/mg, type: 'scalar' }
const getSchemaBits = (sch='') => {
const escapedSchemaWithComments = escapeGraphQlSchemaPlus(sch, carrReturnEsc, tabEsc)
const escapedSchemaWithoutComments = escapeGraphQlSchemaPlus(sch.replace(/#(.*?)\n/g, ''), carrReturnEsc, tabEsc)
return _.flatten([typeRegex, inputRegex, enumRegex, interfaceRegex, abstractRegex, scalarRegex]
.map(rx =>
chain((rx.type == 'scalar' ? escapedSchemaWithoutComments : escapedSchemaWithComments).match(rx.regex) || [])
.next(regexMatches =>
rx.type == 'scalar'
? regexMatches.filter(m => m.indexOf('scalar') == 0 || m.match(/^(?![a-zA-Z0-9])/))
: regexMatches)
.next(regexMatches => {
const transform = rx.type == 'scalar' ? breakdownScalarBit : breakdownSchemabBit
return regexMatches.map(str => transform(str))
})
.val()))
}

const breakdownSchemabBit = str => {
const blockMatch = str.match(/{(.*?)_cr_([^#]*?)}/)
if (!blockMatch) {
const msg = 'Schema error: Missing block'
log(msg)
throw new Error(msg)
}

const block = _.toArray(_(blockMatch[0].replace(/_t_/g, '').replace(/^{/,'').replace(/}$/,'').split(carrReturnEsc).map(x => x.trim())).filter(x => x != ''))
const rawProperty = str.split(carrReturnEsc).join(' ').split(tabEsc).join(' ').replace(/ +(?= )/g,'').trim()
const { property, extend } = rawProperty.indexOf('extend') == 0
? { property: rawProperty.replace('extend ', ''), extend: true }
: { property: rawProperty, extend: false }
return { property, block, extend }
})))))
const block = _.toArray(_(blockMatch[0].replace(/_t_/g, '').replace(/^{/,'').replace(/}$/,'').split(carrReturnEsc).map(x => x.trim())).filter(x => x != ''))
const rawProperty = str.split(carrReturnEsc).join(' ').split(tabEsc).join(' ').replace(/ +(?= )/g,'').trim()
const { property, extend } = rawProperty.indexOf('extend') == 0
? { property: rawProperty.replace('extend ', ''), extend: true }
: { property: rawProperty, extend: false }
return { property, block, extend }
}

const breakdownScalarBit = str => {
const block = (str.split(' ').slice(-1) || [])[0]
return { property: `scalar ${block}`, block: block, extend: false, scalar: true }
}

const getSchemaEntity = firstLine =>
firstLine.indexOf('type') == 0 ? { type: 'TYPE', name: firstLine.replace('type', '').replace(/ /g, '').replace('{', '') } :
Expand Down Expand Up @@ -222,40 +252,53 @@ const getBlockProperties = (blockParts, baseObj, metadata) =>
const getSchemaObject = (definitions, typeName, nameRegEx, metadata) =>
_.toArray(_(definitions).filter(d => d.property.indexOf(typeName) == 0)
.map(d => {
const typeDefMatch = d.property.match(/(.*?){/)
if (!typeDefMatch || typeDefMatch[0].indexOf('#') >= 0) throw new Error(`Schema error: Syntax error in '${d.property}'. Cannot any find schema type definition.`)
const typeDef = typeDefMatch[0]
const nameMatch = typeDef.match(nameRegEx)
if (!nameMatch) throw new Error(`Schema error: ${typeName} with missing name.`)
const name = nameMatch[1].trim().split(' ')[0]
const genericTypeMatch = name.match(genericTypeRegEx)
const isGenericType = genericTypeMatch ? genericTypeMatch[1] : null
const inheritsMatch = typeDef.match(inheritsRegex)
const superClass = inheritsMatch
? inheritsMatch[0].replace('inherits ', '').replace(',', '').trim()
: null
const implementsMatch = typeDef.match(implementsRegex)
const _interface = implementsMatch
? implementsMatch[0].replace('implements ', '').replace('{', '').split(',').map(x => x.trim().split(' ')[0])
: null

const objectType = typeName.toUpperCase()
const metadat = metadata
? _(metadata).filter(m => m.schemaType == objectType && m.schemaName == name).first() || null
: null

const baseObj = { type: objectType, name: name }
const result = {
type: objectType,
extend: d.extend,
name: name,
metadata: metadat,
genericType: isGenericType,
blockProps: getBlockProperties(d.block, baseObj, metadata),
inherits: superClass,
implements: _interface
if (typeName == 'scalar')
return {
type: 'SCALAR',
extend: false,
name: d.block,
metadata: null,
genericType: false,
blockProps: [],
inherits: null,
implements: null
}
else {
const typeDefMatch = d.property.match(/(.*?){/)
if (!typeDefMatch || typeDefMatch[0].indexOf('#') >= 0) throw new Error(`Schema error: Syntax error in '${d.property}'. Cannot any find schema type definition.`)
const typeDef = typeDefMatch[0]
const nameMatch = typeDef.match(nameRegEx)
if (!nameMatch) throw new Error(`Schema error: ${typeName} with missing name.`)
const name = nameMatch[1].trim().split(' ')[0]
const genericTypeMatch = name.match(genericTypeRegEx)
const isGenericType = genericTypeMatch ? genericTypeMatch[1] : null
const inheritsMatch = typeDef.match(inheritsRegex)
const superClass = inheritsMatch
? inheritsMatch[0].replace('inherits ', '').replace(',', '').trim()
: null
const implementsMatch = typeDef.match(implementsRegex)
const _interface = implementsMatch
? implementsMatch[0].replace('implements ', '').replace('{', '').split(',').map(x => x.trim().split(' ')[0])
: null

const objectType = typeName.toUpperCase()
const metadat = metadata
? _(metadata).filter(m => m.schemaType == objectType && m.schemaName == name).first() || null
: null

const baseObj = { type: objectType, name: name }
const result = {
type: objectType,
extend: d.extend,
name: name,
metadata: metadat,
genericType: isGenericType,
blockProps: getBlockProperties(d.block, baseObj, metadata),
inherits: superClass,
implements: _interface
}
return result
}
return result
}))

const getGenericAlias = s => !s ? genericDefaultNameAlias :
Expand All @@ -273,6 +316,8 @@ const getInputs = (definitions, metadata) => getSchemaObject(definitions, 'input

const getEnums = (definitions, metadata) => getSchemaObject(definitions, 'enum', enumNameRegEx, metadata)

const getScalars = (definitions, metadata) => getSchemaObject(definitions, 'scalar', null, metadata)

let memoizedExtendedObject = {}
const getObjWithExtensions = (obj, schemaObjects) => {
if (obj && schemaObjects && obj.inherits) {
Expand Down Expand Up @@ -347,10 +392,10 @@ const addComments = (obj, comments) => {
const parseSchemaObjToString = (comments, type, name, _implements, blockProps, extend=false) =>
[
`${comments && comments != '' ? `\n${comments}` : ''}`,
`${extend ? 'extend ' : ''}${type.toLowerCase()} ${name}${_implements && _implements.length > 0 ? ` implements ${_implements.join(', ')}` : ''} { `,
`${extend ? 'extend ' : ''}${type.toLowerCase()} ${name}${_implements && _implements.length > 0 ? ` implements ${_implements.join(', ')}` : ''} ${blockProps.some(x => x) ? '{': ''} `,
blockProps.map(prop => ` ${prop.comments != '' ? `${prop.comments}\n ` : ''}${prop.value}`).join('\n'),
'}'
].join('\n')
blockProps.some(x => x) ? '}': ''
].filter(x => x).join('\n')

const isTypeGeneric = (type, genericLetter) =>
!type || !genericLetter ? false :
Expand Down Expand Up @@ -408,7 +453,7 @@ const buildSchemaString = schemaObjs => _(schemaObjs)
.join('\n')

const getSchemaParts = (graphQlSchema, metadata, includeNewGenTypes) => chain(getSchemaBits(graphQlSchema))
.next(schemaBits => _([getInterfaces, getAbstracts, getTypes, getInputs, getEnums]
.next(schemaBits => _([getInterfaces, getAbstracts, getTypes, getInputs, getEnums, getScalars]
.reduce((objects, getObjects) => objects.concat(getObjects(schemaBits, metadata)), [])))
.next(firstSchemaBreakDown => _.toArray(firstSchemaBreakDown
.map(obj => getObjWithExtensions(obj, firstSchemaBreakDown))
Expand All @@ -419,7 +464,7 @@ const getSchemaParts = (graphQlSchema, metadata, includeNewGenTypes) => chain(ge
.val()

const resetMemory = () => {
s = null
_s = {}
memoizedGenericSchemaObjects = {}
memoizedExtendedObject = {}
memoizedInterfaceWithAncestors = {}
Expand Down
7 changes: 7 additions & 0 deletions src/utilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ const log = (msg, name) => {
return msg
}
/*eslint-enable */
/**
* Removes all multi-spaces with a single space + replace carriage returns with 'cr' and tabs with 't'
* @param {String} sch Text input
* @param {String} cr Carriage return replacement
* @param {String} t Tab replacement
* @return {String} Escaped text
*/
const escapeGraphQlSchema = (sch, cr, t) => sch.replace(/[\n\r]+/g, cr).replace(/[\t\r]+/g, t).replace(/\s+/g, ' ')
const removeMultiSpaces = s => s.replace(/ +(?= )/g,'')
const matchLeftNonGreedy = (str, startChar, endChar) => chain(str.match(new RegExp(`${startChar}(.*?)${endChar}`)))
Expand Down
118 changes: 118 additions & 0 deletions test/browser/graphqls2s.js
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,124 @@ var runtest = function(s2s, assert) {
assert.equal(answer,correct)
})))

var schema_input_fdwkhj42 = `scalar Date
scalar Like
# This is some description of
# what a Post object is plus an attemp to fool the scalar type.
type Post {
id: ID!
# A name is a property.
name: String!
creationDate: Date
likeRate: Like
}
scalar Strength
type Test { id: ID }
type PostUserRating {
# Rating indicates the rating a user gave
# to a post. Fooling test: type Test { id: ID }
rating: Strength!
}`

var schema_output_fdwkhj42 = `
# This is some description of
# what a Post object is plus an attemp to fool the scalar type.
type Post {
id: ID!
# A name is a property.
name: String!
creationDate: Date
likeRate: Like
}
type Test { id: ID }
type PostUserRating {
# Rating indicates the rating a user gave
# to a post. Fooling test: type Test { id: ID }
rating: Strength!
}
scalar Date
scalar Like
scalar Strength`

/*eslint-disable */
describe('graphqls2s', () =>
describe('#transpileSchema: SUPPORT FOR SCALAR', () =>
it('Should support custom scalar types.', () => {
var output = transpileSchema(schema_input_fdwkhj42)
var answer = compressString(output)
var correct = compressString(schema_output_fdwkhj42)
assert.equal(answer,correct)
})))

var schema_input_fdwfcds95d = `scalar Date
scalar Like
union Product = Bicycle | Racket
# This is some description of
# what a Post object is plus an attemp to fool the scalar type.
type Post {
id: ID!
# A name is a property.
name: String!
creationDate: Date
likeRate: Like
}
scalar Strength
type Test { id: ID }
type PostUserRating {
# Rating indicates the rating a user gave
# to a post. Fooling test: type Test { id: ID }
rating: Strength!
}`

var schema_output_fdwfcds95d = `
# This is some description of
# what a Post object is plus an attemp to fool the scalar type.
type Post {
id: ID!
# A name is a property.
name: String!
creationDate: Date
likeRate: Like
}
type Test { id: ID }
type PostUserRating {
# Rating indicates the rating a user gave
# to a post. Fooling test: type Test { id: ID }
rating: Strength!
}
union Product = Bicycle | Racket
scalar Date
scalar Like
scalar Strength`

/*eslint-disable */
describe('graphqls2s', () =>
describe('#transpileSchema: SUPPORT FOR UNION', () =>
it('Should support union types.', () => {
var output = transpileSchema(schema_input_fdwfcds95d)
console.log(output)
var answer = compressString(output)
var correct = compressString(schema_output_fdwfcds95d)
assert.equal(answer,correct)
})))

/*eslint-disable */
describe('graphqls2s', () =>
describe('#extractGraphMetadata: EXTRACT METADATA', () =>
Expand Down

0 comments on commit 351e2e5

Please sign in to comment.