function generateComponent(typedocHtmlFile, relativeTypedocHtmlFile, demoAppDocsModuleDir) {
  const directory = path.dirname(relativeTypedocHtmlFile);
  mkdirp.sync(path.join(demoAppDocsModuleDir, directory));

  const fileName = path.basename(relativeTypedocHtmlFile);
  const componentHtmlFileName = fileName.replace(/\.html$/, '.component.html');

  const componentHtmlFile = path.join(demoAppDocsModuleDir, directory, componentHtmlFileName);
  const typedocHtmlFileContent = fs.readFileSync(typedocHtmlFile, { encoding: 'utf8' });
  const escapedHtmlFileContent = fixRouterFragments(fixReadmeMdLinks(escapeHtmlForAngular(typedocHtmlFileContent)));

  fs.writeFileSync(componentHtmlFile, escapedHtmlFileContent, { encoding: 'utf8' });

  const componentFileName = fileName.replace(/\.html$/, '.component');
  const componentTsFileName = componentFileName + '.ts';
  const componentTsFile = path.join(demoAppDocsModuleDir, directory, componentTsFileName);

  const componentName = generateComponentName(fileName);
  const componentTsFileContent = `import { Component } from '@angular/core';

@Component({
  templateUrl: './${componentHtmlFileName}'
})
export class ${componentName} { }
`;
  fs.writeFileSync(componentTsFile, componentTsFileContent, { encoding: 'utf8' });

  // Return metadata for generating module file
  return {
    name: componentName,
    file: path.join(directory, componentFileName),
    route: relativeTypedocHtmlFile // route is based on original file name
  };
}

Convert typedoc HTML file into Angular component for use in demo app


function generateComponentName(fileName) {
  const bareName = fileName.replace(/\.html$/, '').replace(/[._]/g, '');
  return bareName.charAt(0).toUpperCase() + bareName.substr(1) + 'Component';
}

function generateComponentName ( fileName ) { const bareName = fileName . replace ( / \.html$ / , '' ) . replace ( / [._] / g , '' ) ; return bareName . charAt ( 0 ) . toUpperCase ( ) + bareName . substr ( 1 ) + 'Component' ; }

Generate component name from HTML file name
It preserves all except for _ and . characters that get added by typedoc
The generated named is still weird, but it doesn't matter

Generate component name from HTML file name It preserves all except for _ and . characters that get added by typedoc The generated named is still weird but it doesn t matter


function generateModuleFile(componentsMetadata, demoAppDocsModuleDir) {
  const imports = componentsMetadata
    .map(componentMetadata => `import { ${componentMetadata.name} } from './${componentMetadata.file}';`)
    .join('\n');

  const components = componentsMetadata
    .map(componentMetadata => `    ${componentMetadata.name},`)
    .join('\n');

  const routes = componentsMetadata
    .map(componentMetadata => `    { path: 'docs/${componentMetadata.route}', component: ${componentMetadata.name} },`)
    .join('\n');

  const moduleTsFileContents = `import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

${imports}

const routes: Routes = [
    { path: 'docs', component: IndexComponent }, // always start with index
${routes}
];

@NgModule({
  imports: [
    RouterModule.forChild(routes),
  ],
  declarations: [
${components}
  ],
  exports: [
${components}
  ]
})
export class DocsModule { }
`;

  const moduleTsFile = path.join(demoAppDocsModuleDir, 'docs.module.ts');
  fs.writeFileSync(moduleTsFile, moduleTsFileContents, { encoding: 'utf8' });
}

function generateModuleFile ( componentsMetadata , demoAppDocsModuleDir ) { const imports = componentsMetadata . map ( componentMetadata => ` ${ componentMetadata . name } ${ componentMetadata . file } ` ) . join ( '\n' ) ; const components = componentsMetadata . map ( componentMetadata => ` ${ componentMetadata . name } ` ) . join ( '\n' ) ; const routes = componentsMetadata . map ( componentMetadata => ` ${ componentMetadata . route } ${ componentMetadata . name } ` ) . join ( '\n' ) ; const moduleTsFileContents = ` ${ imports } ${ routes } ${ components } ${ components } ` ; const moduleTsFile = path . join ( demoAppDocsModuleDir , 'docs.module.ts' ) ; fs . writeFileSync ( moduleTsFile , moduleTsFileContents , { encoding : 'utf8' } ) ; }

Generate module file based on metadata from generated components


function generatePackageJson() {
  const mainFile = path.resolve(__dirname, '../package.json');
  const libTemplateFile = path.resolve(__dirname, '../src/ng5-slider/package.json.template');
  const libFile = path.resolve(__dirname, '../src/ng5-slider/package.json');

  const libTemplateConfig = JSON.parse(fs.readFileSync(libTemplateFile, { encoding: 'utf8' }));
  const mainConfig = JSON.parse(fs.readFileSync(mainFile, { encoding: 'utf8' }));

  let libConfig = {};

  for (let key of libTemplateConfig.keysToCopyFromMainPackageJson) {
      libConfig[key] = mainConfig[key];
  }
  libConfig.dependencies = {};
  for (let dependency of libTemplateConfig.dependenciesToCopyFromMainPackageJson) {
    libConfig.dependencies[dependency] = mainConfig.dependencies[dependency];
  }
  libConfig.peerDependencies = {};
  for (let dependency of libTemplateConfig.dependenciesToCopyAsPeerDependenciesFromMainPackageJson) {
    libConfig.peerDependencies[dependency] = mainConfig.dependencies[dependency];
  }

  libConfig = Object.assign({}, libConfig, libTemplateConfig, libConfig);
  delete libConfig.keysToCopyFromMainPackageJson;
  delete libConfig.dependenciesToCopyFromMainPackageJson;
  delete libConfig.dependenciesToCopyAsPeerDependenciesFromMainPackageJson;

  for (let dependency of libTemplateConfig.dependenciesToCopyFromMainPackageJson) {
    libConfig.ngPackage.whitelistedNonPeerDependencies.push(dependency);
  }

  const prettyPrintedLibConfig = JSON.stringify(libConfig, null, 2);

  fs.writeFileSync(libFile, prettyPrintedLibConfig, { encoding: 'utf8' });
}

Generate package.json file based on package.json.template and main package.json

Generate package . json file based on package . json . template and main package . json


function generatePublicApiTs() {
  const configFile = path.resolve(__dirname, '../src/ng5-slider/lib/public_api.json');
  const tsFile = path.resolve(__dirname, '../src/ng5-slider/lib/public_api.ts');

  const config = JSON.parse(fs.readFileSync(configFile, { encoding: 'utf8' }));

  let tsFileContent = '';

  for (let exportDef of config.exports) {
    if (exportDef.what instanceof Array) {
      const whats = exportDef.what.join(', ');
      tsFileContent += `export { ${whats} } from '${exportDef.file}';\n`;
    } else {
      tsFileContent += `export ${exportDef.what} from '${exportDef.file}';\n`;
    }
  }

  fs.writeFileSync(tsFile, tsFileContent, {encoding: 'utf8'});
}

function
generatePublicApiTs
(
)
{
const
configFile
=
path
.
resolve
(
__dirname
,
'../src/ng5-slider/lib/public_api.json'
)
;
const
tsFile
=
path
.
resolve
(
__dirname
,
'../src/ng5-slider/lib/public_api.ts'
)
;
const
config
=
JSON
.
parse
(
fs
.
readFileSync
(
configFile
,
{
encoding
:
'utf8'
}
)
)
;
let
tsFileContent
=
''
;
for
(
let
exportDef
of
config
.
exports
)
{
if
(
exportDef
.
what
instanceof
Array
)
{
const
whats
=
exportDef
.
what
.
join
(
', '
)
;
tsFileContent
+=
`
${
whats
}
${
exportDef
.
file
}
\n
`
;
}
else
{
tsFileContent
+=
`
${
exportDef
.
what
}
${
exportDef
.
file
}
\n
`
;
}
}
fs
.
writeFileSync
(
tsFile
,
tsFileContent
,
{
encoding
:
'utf8'
}
)
;
}

Convert public_api.json to public_api.ts

Convert public_api . json to public_api . ts


function readdirRecursivelySync(dir) {
  let results = [];
  const list = fs.readdirSync(dir);
  for (let file of list) {
    file = path.resolve(dir, file);
    const stat = fs.statSync(file);
    if (stat && stat.isDirectory()) {
      results = results.concat(readdirRecursivelySync(file));
    } else {
      results.push(file);
    }
  }
  return results;
}

function readdirRecursivelySync ( dir ) { let results = [ ] ; const list = fs . readdirSync ( dir ) ; for ( let file of list ) { file = path . resolve ( dir , file ) ; const stat = fs . statSync ( file ) ; if ( stat && stat . isDirectory ( ) ) { results = results . concat ( readdirRecursivelySync ( file ) ) ; } else { results . push ( file ) ; } } return results ; }

Get all files in directory recursively, synchronously

Get all files in directory recursively synchronously


function copyReadmeMd(sourceReadmeMd) {
  const libReadmeFile = path.resolve(__dirname, '../src/ng5-slider/README.md');

  const sourceReadme = fs.readFileSync(sourceReadmeMd, { encoding: 'utf8'});
  fs.writeFileSync(libReadmeFile, sourceReadme, {encoding: 'utf8'});
}

function copyReadmeMd ( sourceReadmeMd ) { const libReadmeFile = path . resolve ( __dirname , '../src/ng5-slider/README.md' ) ; const sourceReadme = fs . readFileSync ( sourceReadmeMd , { encoding : 'utf8' } ) ; fs . writeFileSync ( libReadmeFile , sourceReadme , { encoding : 'utf8' } ) ; }

Copy README.md from given location to the library directory

Copy README . md from given location to the library directory


function generateTemplate(templateFile, snippetsDir) {
  const titleTemplateFile = templateFile.replace('.template.html', '.title-template.html');
  const outputTemplateFile = templateFile.replace('.template.html', '.html');
  const codeFile = templateFile.replace('.template.html', '.ts');
  const styleFile = templateFile.replace('.template.html', '.scss');

  const titleTemplateFileContent = fs.readFileSync(path.resolve(snippetsDir, titleTemplateFile), { encoding: 'utf8' });

  const templateFileContent = fs.readFileSync(path.resolve(snippetsDir, templateFile), { encoding: 'utf8' });
  const templateTabHtml = tabHtml(path.basename(outputTemplateFile), templateFileContent, 'html');

  let codeFileContent = fs.readFileSync(path.resolve(snippetsDir, codeFile), { encoding: 'utf8' });
  // The only modification to the source file is to remove the @local prefix from slider import
  codeFileContent = codeFileContent.replace(/@local\/ng5-slider/g, "ng5-slider");
  const codeTabHtml = tabHtml(path.basename(codeFile), codeFileContent, 'typescript');

  let styleTabHtml = '';
  if (fs.existsSync(path.resolve(snippetsDir, styleFile))) {
    const styleFileContent = fs.readFileSync(path.resolve(snippetsDir, styleFile), { encoding: 'utf8' });
    styleTabHtml = tabHtml(path.basename(styleFile), styleFileContent, 'scss');
  }

  const outputHtmlFileContent = `
${titleTemplateFileContent}

      ${templateFileContent}

    <ngb-tabset class="snippet-code-tabset">
      ${codeTabHtml}

      ${templateTabHtml}

      ${styleTabHtml}
    </ngb-tabset>

`;

  fs.writeFileSync(path.resolve(snippetsDir, outputTemplateFile), outputHtmlFileContent, { encoding: 'utf8' });
}


function highlight(code, lang) {
  return prism.highlight(code.trim(), prism.languages[lang]);
}

function highlight ( code , lang ) { return prism . highlight ( code . trim ( ) , prism . languages [ lang ] ) ; }

Generate highlighted source code using prism


function tabHtml(tabTitle, codeContent, codeLang) {
  return `<ngb-tab title="${escape(tabTitle)}">
      <ng-template ngbTabContent>

${escapeBraces(highlight(codeContent, codeLang))}

      </ng-template>
    </ngb-tab>`;
}

function tabHtml ( tabTitle , codeContent , codeLang ) { return ` ${ escape ( tabTitle ) } ${ codeLang } ${ codeLang } ${ escapeBraces ( highlight ( codeContent , codeLang ) ) } ` ; }


function nomalizeScope(initialScope, node) {
    let scope = getInnermostScope(initialScope, node)

    while (scope && scope.block === node) {
        scope = scope.upper
    }

    return scope
}

function nomalizeScope ( initialScope , node ) { let scope = getInnermostScope ( initialScope , node ) while ( scope && scope . block === node ) { scope = scope . upper } return scope }

Find the scope that a given node belongs to.
@param {Scope} initialScope The initial scope to find.
@param {Node} node The AST node.
@returns {Scope} The scope that the node belongs to.

Find the scope that a given node belongs to .


function merge(x, y) {
    for (const key of Object.keys(y)) {
        if (typeof x[key] === "function") {
            if (x[key]._handlers == null) {
                const fs = [x[key], y[key]]
                x[key] = dispatch.bind(null, fs)
                x[key]._handlers = fs
            } else {
                x[key]._handlers.push(y[key])
            }
        } else {
            x[key] = y[key]
        }
    }
    return x
}

function merge ( x , y ) { for ( const key of Object . keys ( y ) ) { if ( typeof x [ key ] === "function" ) { if ( x [ key ] . _handlers == null ) { const fs = [ x [ key ] , y [ key ] ] x [ key ] = dispatch . bind ( null , fs ) x [ key ] . _handlers = fs } else { x [ key ] . _handlers . push ( y [ key ] ) } } else { x [ key ] = y [ key ] } } return x }

Merge two visitors.
@param {Visitor} x The visitor which is assigned.
@param {Visitor} y The visitor which is assigning.
@returns {Visitor} `x`.


function defineVisitor(context, options) {
    const testInfoPrototype = {
        get isStrict() {
            return nomalizeScope(context.getScope(), this.node).isStrict
        },
    }

    /**
     * Check whether a given case object is full-supported on the configured node version.
     * @param {{supported:string}} aCase The case object to check.
     * @returns {boolean} `true` if it's supporting.
     */
    function isNotSupportingVersion(aCase) {
        return (
            !aCase.supported ||
            options.version.intersects(getSemverRange(`<${aCase.supported}`))
        )
    }

    /**
     * Define the predicate function to check whether a given case object is supported on the configured node version.
     * @param {Node} node The node which is reported.
     * @returns {function(aCase:{supported:string}):boolean} The predicate function.
     */
    function isNotSupportingOn(node) {
        return aCase =>
            isNotSupportingVersion(aCase) &&
            (!aCase.test || aCase.test({ node, __proto__: testInfoPrototype }))
    }

    return (
        keywords
            // Omit full-supported features and ignored features by options
            // because this rule never reports those.
            .filter(
                keyword =>
                    !options.ignores.has(keyword) &&
                    features[keyword].cases.some(isNotSupportingVersion)
            )
            // Merge remaining features with overriding `context.report()`.
            .reduce((visitor, keyword) => {
                const { ruleId, cases } = features[keyword]
                const rule = esRules[ruleId]
                const thisContext = {
                    __proto__: context,

                    // Override `context.report()` then:
                    // - ignore if it's supported.
                    // - override reporting messages.
                    report(descriptor) {
                        // Set additional information.
                        if (descriptor.data) {
                            descriptor.data.version = options.version.raw
                        } else {
                            descriptor.data = { version: options.version.raw }
                        }
                        descriptor.fix = undefined

                        // Test and report.
                        const node = descriptor.node
                        const hitCase = cases.find(isNotSupportingOn(node))
                        if (hitCase) {
                            descriptor.messageId = hitCase.messageId
                            descriptor.data.supported = hitCase.supported
                            super.report(descriptor)
                        }
                    },
                }
                return merge(visitor, rule.create(thisContext))
            }, {})
    )
}

Define the visitor object as merging the rules of eslint-plugin-es.
@param {RuleContext} context The rule context.
@param {{version:Range,ignores:Set<string>}} options The options.
@returns {object} The defined visitor.

Define the visitor object as merging the rules of eslint - plugin - es .


function isNotSupportingVersion(aCase) {
        return (
            !aCase.supported ||
            options.version.intersects(getSemverRange(`<${aCase.supported}`))
        )
    }

function isNotSupportingVersion ( aCase ) { return ( ! aCase . supported || options . version . intersects ( getSemverRange ( ` ${ aCase . supported } ` ) ) ) }

Check whether a given case object is full-supported on the configured node version.
@param {{supported:string}} aCase The case object to check.
@returns {boolean} `true` if it's supporting.

Check whether a given case object is full - supported on the configured node version .


function isNotSupportingOn(node) {
        return aCase =>
            isNotSupportingVersion(aCase) &&
            (!aCase.test || aCase.test({ node, __proto__: testInfoPrototype }))
    }

function isNotSupportingOn ( node ) { return aCase => isNotSupportingVersion ( aCase ) && ( ! aCase . test || aCase . test ( { node , __proto__ : testInfoPrototype } ) ) }

Define the predicate function to check whether a given case object is supported on the configured node version.
@param {Node} node The node which is reported.
@returns {function(aCase:{supported:string}):boolean} The predicate function.

Define the predicate function to check whether a given case object is supported on the configured node version .


function readPackageJson(dir) {
    const filePath = path.join(dir, "package.json")
    try {
        const text = fs.readFileSync(filePath, "utf8")
        const data = JSON.parse(text)

        if (typeof data === "object" && data !== null) {
            data.filePath = filePath
            return data
        }
    } catch (_err) {
        // do nothing.
    }

    return null
}

function
readPackageJson
(
dir
)
{
const
filePath
=
path
.
join
(
dir
,
"package.json"
)
try
{
const
text
=
fs
.
readFileSync
(
filePath
,
"utf8"
)
const
data
=
JSON
.
parse
(
text
)
if
(
typeof
data
===
"object"
&&
data
!==
)
{
data
.
filePath
=
filePath
return
data
}
}
catch
(
_err
)
{
// do nothing.
}
return
}

Reads the `package.json` data in a given path.

Don't cache the data.

@param {string} dir - The path to a directory to read.
@returns {object|null} The read `package.json` data, or null.

Reads the package . json data in a given path .


function get(option) {
    if (option && option.allowModules && Array.isArray(option.allowModules)) {
        return option.allowModules.map(String)
    }
    return null
}

function get ( option ) { if ( option && option . allowModules && Array . isArray ( option . allowModules ) ) { return option . allowModules . map ( String ) } return null }

Gets `allowModules` property from a given option object.

@param {object|undefined} option - An option object to get.
@returns {string[]|null} The `allowModules` value, or `null`.

Gets allowModules property from a given option object .


function getTopAssignment(leafNode) {
    let node = leafNode

    // Skip MemberExpressions.
    while (
        node.parent.type === "MemberExpression" &&
        node.parent.object === node
    ) {
        node = node.parent
    }

    // Check assignments.
    if (!isAssignee(node)) {
        return null
    }

    // Find the top.
    while (node.parent.type === "AssignmentExpression") {
        node = node.parent
    }

    return node
}

function
getTopAssignment
(
leafNode
)
{
let
node
=
leafNode
// Skip MemberExpressions.
while
(
node
.
parent
.
type
===
"MemberExpression"
&&
node
.
parent
.
object
===
node
)
{
node
=
node
.
parent
}
// Check assignments.
if
(
!
isAssignee
(
node
)
)
{
return
}
// Find the top.
while
(
node
.
parent
.
type
===
"AssignmentExpression"
)
{
node
=
node
.
parent
}
return
node
}

Gets the top assignment expression node if the given node is an assignee.

This is used to distinguish 2 assignees belong to the same assignment.
If the node is not an assignee, this returns null.

@param {ASTNode} leafNode - The node to get.
@returns {ASTNode|null} The top assignment expression node, or null.

Gets the top assignment expression node if the given node is an assignee .


function getModuleExportsNodes(scope) {
    const variable = scope.set.get("module")
    if (variable == null) {
        return []
    }
    return variable.references
        .map(reference => reference.identifier.parent)
        .filter(
            node =>
                node.type === "MemberExpression" &&
                getStaticPropertyName(node) === "exports"
        )
}

function getModuleExportsNodes ( scope ) { const variable = scope . set . get ( "module" ) if ( variable == null ) { return [ ] } return variable . references . map ( reference => reference . identifier . parent ) . filter ( node => node . type === "MemberExpression" && getStaticPropertyName ( node ) === "exports" ) }

Gets the reference of `module.exports` from the given scope.

@param {escope.Scope} scope - The scope to get.
@returns {ASTNode[]} Gotten MemberExpression node list.

Gets the reference of module . exports from the given scope .


function getExportsNodes(scope) {
    const variable = scope.set.get("exports")
    if (variable == null) {
        return []
    }
    return variable.references.map(reference => reference.identifier)
}

function getExportsNodes ( scope ) { const variable = scope . set . get ( "exports" ) if ( variable == null ) { return [ ] } return variable . references . map ( reference => reference . identifier ) }

Gets the reference of `exports` from the given scope.

@param {escope.Scope} scope - The scope to get.
@returns {ASTNode[]} Gotten Identifier node list.

Gets the reference of exports from the given scope .


function getLocation(node) {
            const token = sourceCode.getTokenAfter(node)
            return {
                start: node.loc.start,
                end: token.loc.end,
            }
        }

function getLocation ( node ) { const token = sourceCode . getTokenAfter ( node ) return { start : node . loc . start , end : token . loc . end , } }

Gets the location info of reports.

exports = foo
^^^^^^^^^

module.exports = foo
^^^^^^^^^^^^^^^^

@param {ASTNode} node - The node of `exports`/`module.exports`.
@returns {Location} The location info of reports.


function enforceModuleExports() {
            const globalScope = context.getScope()
            const exportsNodes = getExportsNodes(globalScope)
            const assignList = batchAssignAllowed
                ? createAssignmentList(getModuleExportsNodes(globalScope))
                : []

            for (const node of exportsNodes) {
                // Skip if it's a batch assignment.
                if (
                    assignList.length > 0 &&
                    assignList.indexOf(getTopAssignment(node)) !== -1
                ) {
                    continue
                }

                // Report.
                context.report({
                    node,
                    loc: getLocation(node),
                    message:
                        "Unexpected access to 'exports'. Use 'module.exports' instead.",
                })
            }
        }

Enforces `module.exports`.
This warns references of `exports`.

@returns {void}

Enforces module . exports . This warns references of exports .


function enforceExports() {
            const globalScope = context.getScope()
            const exportsNodes = getExportsNodes(globalScope)
            const moduleExportsNodes = getModuleExportsNodes(globalScope)
            const assignList = batchAssignAllowed
                ? createAssignmentList(exportsNodes)
                : []
            const batchAssignList = []

            for (const node of moduleExportsNodes) {
                // Skip if it's a batch assignment.
                if (assignList.length > 0) {
                    const found = assignList.indexOf(getTopAssignment(node))
                    if (found !== -1) {
                        batchAssignList.push(assignList[found])
                        assignList.splice(found, 1)
                        continue
                    }
                }

                // Report.
                context.report({
                    node,
                    loc: getLocation(node),
                    message:
                        "Unexpected access to 'module.exports'. Use 'exports' instead.",
                })
            }

            // Disallow direct assignment to `exports`.
            for (const node of exportsNodes) {
                // Skip if it's not assignee.
                if (!isAssignee(node)) {
                    continue
                }

                // Check if it's a batch assignment.
                if (batchAssignList.indexOf(getTopAssignment(node)) !== -1) {
                    continue
                }

                // Report.
                context.report({
                    node,
                    loc: getLocation(node),
                    message:
                        "Unexpected assignment to 'exports'. Don't modify 'exports' itself.",
                })
            }
        }

Enforces `exports`.
This warns references of `module.exports`.

@returns {void}

Enforces exports . This warns references of module . exports .


function filterNeverIgnoredFiles(p) {
    const basedir = path.dirname(p.filePath)
    const mainFilePath =
        typeof p.main === "string" ? path.join(basedir, p.main) : null

    return filePath =>
        path.join(basedir, filePath) !== mainFilePath &&
        filePath !== "package.json" &&
        !NEVER_IGNORED.test(path.relative(basedir, filePath))
}

function filterNeverIgnoredFiles ( p ) { const basedir = path . dirname ( p . filePath ) const mainFilePath = typeof p . main === "string" ? path . join ( basedir , p . main ) : null return filePath => path . join ( basedir , filePath ) !== mainFilePath && filePath !== "package.json" && ! NEVER_IGNORED . test ( path . relative ( basedir , filePath ) ) }

Creates a function which checks whether or not a given file is ignoreable.

@param {object} p - An object of package.json.
@returns {function} A function which checks whether or not a given file is ignoreable.

Creates a function which checks whether or not a given file is ignoreable .


function normalizeValue(x) {
    if (Array.isArray(x)) {
        return x
    }

    return Object.keys(x).map(pattern => ({
        include: [pattern],
        exclude: [],
        replace: x[pattern],
    }))
}

function normalizeValue ( x ) { if ( Array . isArray ( x ) ) { return x } return Object . keys ( x ) . map ( pattern => ( { include : [ pattern ] , exclude : [ ] , replace : x [ pattern ] , } ) ) }

Converts old-style value to new-style value.

@param {any} x - The value to convert.
@returns {({include: string[], exclude: string[], replace: string[]})[]} Normalized value.

Converts old - style value to new - style value .


function createMatch(includePatterns, excludePatterns) {
    const include = includePatterns.map(pattern => new Minimatch(pattern))
    const exclude = excludePatterns.map(pattern => new Minimatch(pattern))

    return filePath =>
        include.some(m => m.match(filePath)) &&
        !exclude.some(m => m.match(filePath))
}

function createMatch ( includePatterns , excludePatterns ) { const include = includePatterns . map ( pattern => new Minimatch ( pattern ) ) const exclude = excludePatterns . map ( pattern => new Minimatch ( pattern ) ) return filePath => include . some ( m => m . match ( filePath ) ) && ! exclude . some ( m => m . match ( filePath ) ) }

Creates the function which checks whether a file path is matched with the given pattern or not.

@param {string[]} includePatterns - The glob patterns to include files.
@param {string[]} excludePatterns - The glob patterns to exclude files.
@returns {function} Created predicate function.

Creates the function which checks whether a file path is matched with the given pattern or not .


function combine(converters) {
    return filePath => {
        for (const converter of converters) {
            if (converter.match(filePath)) {
                return converter.convert(filePath)
            }
        }
        return filePath
    }
}

function combine ( converters ) { return filePath => { for ( const converter of converters ) { if ( converter . match ( filePath ) ) { return converter . convert ( filePath ) } } return filePath } }

Combines given converters.
The result function converts a given path with the first matched converter.

@param {{match: function, convert: function}} converters - A list of converters to combine.
@returns {function} A function which replaces a given path.

Combines given converters . The result function converts a given path with the first matched converter .


function parse(option) {
    if (
        !option ||
        !option.convertPath ||
        typeof option.convertPath !== "object"
    ) {
        return null
    }

    const converters = []
    for (const pattern of normalizeValue(option.convertPath)) {
        const include = toStringArray(pattern.include)
        const exclude = toStringArray(pattern.exclude)
        const fromRegexp = new RegExp(String(pattern.replace[0])) //eslint-disable-line require-unicode-regexp
        const toStr = String(pattern.replace[1])

        converters.push({
            match: createMatch(include, exclude),
            convert: defineConvert(fromRegexp, toStr),
        })
    }

    return combine(converters)
}

Parses `convertPath` property from a given option object.

@param {object|undefined} option - An option object to get.
@returns {function|null} A function which converts a path., or `null`.

Parses convertPath property from a given option object .


function toName(type, path) {
    const baseName = path.join(".")
    return type === ReferenceTracker.CALL
        ? `${baseName}()`
        : type === ReferenceTracker.CONSTRUCT
            ? `new ${baseName}()`
            : baseName
}

function toName ( type , path ) { const baseName = path . join ( "." ) return type === ReferenceTracker . CALL ? ` ${ baseName } ` : type === ReferenceTracker . CONSTRUCT ? ` ${ baseName } ` : baseName }

Convert a given path to name.
@param {symbol} type The report type.
@param {string[]} path The property access path.
@returns {string} The name.


function reportItem(node, name, info) {
            context.report({
                node,
                loc: node.loc,
                message:
                    "{{name}} was deprecated since v{{version}}{{replace}}.",
                data: {
                    name,
                    version: info.since,
                    replace: toReplaceMessage(info.replacedBy),
                },
            })
        }

Reports a use of a deprecated API.

@param {ASTNode} node - A node to report.
@param {string} name - The name of a deprecated API.
@param {{since: number, replacedBy: string}} info - Information of the API.
@returns {void}


function get(option) {
    if (option && option.tryExtensions && Array.isArray(option.tryExtensions)) {
        return option.tryExtensions.map(String)
    }
    return null
}

function get ( option ) { if ( option && option . tryExtensions && Array . isArray ( option . tryExtensions ) ) { return option . tryExtensions . map ( String ) } return null }

Gets `tryExtensions` property from a given option object.

@param {object|undefined} option - An option object to get.
@returns {string[]|null} The `tryExtensions` value, or `null`.

Gets tryExtensions property from a given option object .


function getDefaultVersion(filename) {
    const info = getPackageJson(filename)
    const nodeVersion = info && info.engines && info.engines.node

    return semver.validRange(nodeVersion) || DEFAULT_VERSION
}

function getDefaultVersion ( filename ) { const info = getPackageJson ( filename ) const nodeVersion = info && info . engines && info . engines . node return semver . validRange ( nodeVersion ) || DEFAULT_VERSION }

Gets default version configuration of this rule.

This finds and reads 'package.json' file, then parses 'engines.node' field.
If it's nothing, this returns null.

@param {string} filename - The file name of the current linting file.
@returns {string} The default version configuration.

Gets default version configuration of this rule .


function getIgnoresEnum() {
    return Object.keys(
        OPTIONS.reduce((retv, key) => {
            for (const alias of FEATURES[key].alias) {
                retv[alias] = true
            }
            retv[key] = true
            return retv
        }, Object.create(null))
    )
}

function getIgnoresEnum ( ) { return Object . keys ( OPTIONS . reduce ( ( retv , key ) => { for ( const alias of FEATURES [ key ] . alias ) { retv [ alias ] = true } retv [ key ] = true return retv } , Object . create ( null ) ) ) }

Gets values of the `ignores` option.

@returns {string[]} Values of the `ignores` option.


function isIgnored(key, ignores) {
    return (
        ignores.indexOf(key) !== -1 ||
        FEATURES[key].alias.some(alias => ignores.indexOf(alias) !== -1)
    )
}

function isIgnored ( key , ignores ) { return ( ignores . indexOf ( key ) !== - 1 || FEATURES [ key ] . alias . some ( alias => ignores . indexOf ( alias ) !== - 1 ) ) }

Checks whether a given key should be ignored or not.

@param {string} key - A key to check.
@param {string[]} ignores - An array of keys and aliases to be ignored.
@returns {boolean} `true` if the key should be ignored.

Checks whether a given key should be ignored or not .


function hasPattern(s, pattern) {
    const m = pattern.exec(s)
    return m != null && (m[1] || "").length % 2 === 0
}

function hasPattern ( s , pattern ) { const m = pattern . exec ( s ) return m != null && ( m [ 1 ] || "" ) . length % 2 === 0 }

Check a given string has a given pattern.
@param {string} s A string to check.
@param {RegExp} pattern A RegExp object to check.
@returns {boolean} `true` if the string has the pattern.

Check a given string has a given pattern .


function hasTrailingCommaForFunction(node) {
            const length = node.params.length

            return (
                length >= 1 &&
                sourceCode.getTokenAfter(node.params[length - 1]).value === ","
            )
        }

function hasTrailingCommaForFunction ( node ) { const length = node . params . length return ( length >= 1 && sourceCode . getTokenAfter ( node . params [ length - 1 ] ) . value === "," ) }

Checks whether the given function has trailing commas or not.

@param {ASTNode} node - The function node to check.
@returns {boolean} `true` if the function has trailing commas.

Checks whether the given function has trailing commas or not .


function extendsNull(node) {
            return (
                node.superClass != null &&
                node.superClass.type === "Literal" &&
                node.superClass.value === null
            )
        }

function extendsNull ( node ) { return ( node . superClass != null && node . superClass . type === "Literal" && node . superClass . value === null ) }

Checks whether the given class extends from null or not.

@param {ASTNode} node - The class node to check.
@returns {boolean} `true` if the class extends from null.

Checks whether the given class extends from null or not .


function report(node, key) {
            const version = supportInfo.version
            const feature = supportInfo.features[key]
            if (feature.supported) {
                return
            }

            if (!feature.supportedInStrict) {
                context.report({
                    node,
                    message:
                        "{{feature}} {{be}} not supported yet on Node {{version}}.",
                    data: {
                        feature: feature.name,
                        be: feature.singular ? "is" : "are",
                        version,
                    },
                })
            } else if (!nomalizeScope(context.getScope(), node).isStrict) {
                context.report({
                    node,
                    message:
                        "{{feature}} {{be}} not supported yet on Node {{version}}.",
                    data: {
                        feature: `${feature.name} in non-strict mode`,
                        be: feature.singular ? "is" : "are",
                        version,
                    },
                })
            }
        }

Reports a given node if the specified feature is not supported.

@param {ASTNode} node - A node to be reported.
@param {string} key - A feature name to report.
@returns {void}

Reports a given node if the specified feature is not supported .


function validateRegExp(pattern, flags, node) {
            if (typeof pattern === "string") {
                if (hasPattern(pattern, REGEXP_NAMED_GROUP)) {
                    report(node, "regexpNamedCaptureGroups")
                }
                if (hasPattern(pattern, REGEXP_LOOKBEHIND)) {
                    report(node, "regexpLookbehind")
                }
                if (hasPattern(pattern, REGEXP_UNICODE_PROPERTY)) {
                    report(node, "regexpUnicodeProperties")
                }
            }
            if (typeof flags === "string") {
                if (flags.indexOf("y") !== -1) {
                    report(node, "regexpY")
                }
                if (flags.indexOf("u") !== -1) {
                    report(node, "regexpU")
                }
                if (flags.indexOf("s") !== -1) {
                    report(node, "regexpS")
                }
            }
        }

function validateRegExp ( pattern , flags , node ) { if ( typeof pattern === "string" ) { if ( hasPattern ( pattern , REGEXP_NAMED_GROUP ) ) { report ( node , "regexpNamedCaptureGroups" ) } if ( hasPattern ( pattern , REGEXP_LOOKBEHIND ) ) { report ( node , "regexpLookbehind" ) } if ( hasPattern ( pattern , REGEXP_UNICODE_PROPERTY ) ) { report ( node , "regexpUnicodeProperties" ) } } if ( typeof flags === "string" ) { if ( flags . indexOf ( "y" ) !== - 1 ) { report ( node , "regexpY" ) } if ( flags . indexOf ( "u" ) !== - 1 ) { report ( node , "regexpU" ) } if ( flags . indexOf ( "s" ) !== - 1 ) { report ( node , "regexpS" ) } } }

Validate RegExp syntax.
@param {string} pattern A RegExp pattern to check.
@param {string} flags A RegExp flags to check.
@param {ASTNode} node A node to report.
@returns {void}


function validateRegExpLiteral(node) {
            validateRegExp(node.regex.pattern, node.regex.flags, node)
        }

function validateRegExpLiteral ( node ) { validateRegExp ( node . regex . pattern , node . regex . flags , node ) }

Validate RegExp syntax in a RegExp literal.
@param {ASTNode} node A Literal node to check.
@returns {void}

Validate RegExp syntax in a RegExp literal .


function getFilePath(isModule, id, options) {
    try {
        return resolve.sync(id, options)
    } catch (_err) {
        if (isModule) {
            return null
        }
        return path.resolve(options.basedir, id)
    }
}

function getFilePath ( isModule , id , options ) { try { return resolve . sync ( id , options ) } catch ( _err ) { if ( isModule ) { return null } return path . resolve ( options . basedir , id ) } }

Resolve the given id to file paths.
@param {boolean} isModule The flag which indicates this id is a module.
@param {string} id The id to resolve.
@param {object} options The options of node-resolve module.
It requires `options.basedir`.
@returns {string|null} The resolved path.


function getModuleName(nameOrPath) {
    let end = nameOrPath.indexOf("/")
    if (end !== -1 && nameOrPath[0] === "@") {
        end = nameOrPath.indexOf("/", 1 + end)
    }

    return end === -1 ? nameOrPath : nameOrPath.slice(0, end)
}

function getModuleName ( nameOrPath ) { let end = nameOrPath . indexOf ( "/" ) if ( end !== - 1 && nameOrPath [ 0 ] === "@" ) { end = nameOrPath . indexOf ( "/" , 1 + end ) } return end === - 1 ? nameOrPath : nameOrPath . slice ( 0 , end ) }

Gets the module name of a given path.

e.g. `eslint/lib/ast-utils` -> `eslint`

@param {string} nameOrPath - A path to get.
@returns {string} The module name of the path.


function isBinFile(filePath, binField, basedir) {
    if (!binField) {
        return false
    }
    if (typeof binField === "string") {
        return filePath === path.resolve(basedir, binField)
    }
    return Object.keys(binField).some(
        key => filePath === path.resolve(basedir, binField[key])
    )
}

function isBinFile ( filePath , binField , basedir ) { if ( ! binField ) { return false } if ( typeof binField === "string" ) { return filePath === path . resolve ( basedir , binField ) } return Object . keys ( binField ) . some ( key => filePath === path . resolve ( basedir , binField [ key ] ) ) }

Checks whether or not a given path is a `bin` file.

@param {string} filePath - A file path to check.
@param {string|object|undefined} binField - A value of the `bin` field of `package.json`.
@param {string} basedir - A directory path that `package.json` exists.
@returns {boolean} `true` if the file is a `bin` file.

Checks whether or not a given path is a bin file .


function getExistingExtensions(filePath) {
    const basename = path.basename(filePath, path.extname(filePath))
    try {
        return fs
            .readdirSync(path.dirname(filePath))
            .filter(
                filename =>
                    path.basename(filename, path.extname(filename)) === basename
            )
            .map(filename => path.extname(filename))
    } catch (_error) {
        return []
    }
}

function getExistingExtensions ( filePath ) { const basename = path . basename ( filePath , path . extname ( filePath ) ) try { return fs . readdirSync ( path . dirname ( filePath ) ) . filter ( filename => path . basename ( filename , path . extname ( filename ) ) === basename ) . map ( filename => path . extname ( filename ) ) } catch ( _error ) { return [ ] } }

Get all file extensions of the files which have the same basename.
@param {string} filePath The path to the original file to check.
@returns {string[]} File extensions.

Get all file extensions of the files which have the same basename .


function getEnginesNode(filename) {
    const info = getPackageJson(filename)
    return getSemverRange(info && info.engines && info.engines.node)
}

function getEnginesNode ( filename ) { const info = getPackageJson ( filename ) return getSemverRange ( info && info . engines && info . engines . node ) }

Get the `engines.node` field of package.json.
@param {string} filename The path to the current linting file.
@returns {Range|null} The range object of the `engines.node` field.

Get the engines . node field of package . json .


function existsCaseSensitive(filePath) {
    let dirPath = filePath

    while (dirPath !== "" && !ROOT.test(dirPath)) {
        const fileName = path.basename(dirPath)
        dirPath = path.dirname(dirPath)

        if (fs.readdirSync(dirPath).indexOf(fileName) === -1) {
            return false
        }
    }

    return true
}

function existsCaseSensitive ( filePath ) { let dirPath = filePath while ( dirPath !== "" && ! ROOT . test ( dirPath ) ) { const fileName = path . basename ( dirPath ) dirPath = path . dirname ( dirPath ) if ( fs . readdirSync ( dirPath ) . indexOf ( fileName ) === - 1 ) { return false } } return true }

Check whether the file exists or not.
@param {string} filePath The file path to check.
@returns {boolean} `true` if the file exists.


function get(option) {
    if (option && option.resolvePaths && Array.isArray(option.resolvePaths)) {
        return option.resolvePaths.map(String)
    }
    return null
}

function get ( option ) { if ( option && option . resolvePaths && Array . isArray ( option . resolvePaths ) ) { return option . resolvePaths . map ( String ) } return null }

Gets `resolvePaths` property from a given option object.

@param {object|undefined} option - An option object to get.
@returns {string[]|null} The `allowModules` value, or `null`.

Gets resolvePaths property from a given option object .


function checkFile(path) {
  const formatter = path.match(/\.md$/) ? prettierFormat : clangFormat;
  const source = fs.readFileSync(path, 'utf8');
  const formatted = formatter(path, source);
  if (source !== formatted) {
    return formatted;
  }
  return null;
}

function checkFile ( path ) { const formatter = path . match ( / \.md$ / ) ? prettierFormat : clangFormat ; const source = fs . readFileSync ( path , 'utf8' ) ; const formatted = formatter ( path , source ) ; if ( source !== formatted ) { return formatted ; } return null ; }

Checks that a path is formatted.
@return new text of file if it needs formatting or null otherwise.


function serializeAttrs(attributes, opts) {
    if (!attributes) {
        return;
    }

    let output = '';
    let value;
    Object.keys(attributes).forEach(key => {
        value = attributes[key];
        if (output) {
            output += ' ';
        }

        let isBoolAttr = value && typeof value === 'boolean';
        if (isBoolAttr) {
            output += key;
        }
        else {
            output += key + '="' + (opts.decodeEntities ? entities.encodeXML(value) : value) + '"';
        }
    });

    return output;
}

function
serializeAttrs
(
attributes
,
opts
)
{
if
(
!
attributes
)
{
return
;
}
let
output
=
''
;
let
value
;
Object
.
keys
(
attributes
)
.
forEach
(
key
=>
{
value
=
attributes
[
key
]
;
if
(
output
)
{
output
+=
' '
;
}
let
isBoolAttr
=
value
&&
typeof
value
===
'boolean'
;
if
(
isBoolAttr
)
{
output
+=
key
;
}
else
{
output
+=
key
+
'="'
+
(
opts
.
decodeEntities
entities
.
encodeXML
(
value
)
:
value
)
+
'"'
;
}
}
)
;
return
output
;
}

Serialize tag attributes info

@inner
@param {Object} attributes tag attributes info
@param {Object} opts the serialize options
@return {string}


function getRequireExpressionModuleId(node, t) {
    if (t.isCallExpression(node)) {
        let {arguments: args, callee} = node;
        if (t.isIdentifier(callee)
            && callee.name === 'require'
            && args.length === 1
            && t.isLiteral(args[0])
        ) {
            let id = args[0].value;
            return id;
        }
    }
}

function getRequireExpressionModuleId ( node , t ) { if ( t . isCallExpression ( node ) ) { let { arguments : args , callee } = node ; if ( t . isIdentifier ( callee ) && callee . name === 'require' && args . length === 1 && t . isLiteral ( args [ 0 ] ) ) { let id = args [ 0 ] . value ; return id ; } } }

Get require expression module id

@inner
@param {Object} node the require call expression node
@param {Object} t the bable type definition
@return {?string}


function removeVariableDeclaration(t, bindVar, removeOpts) {
    let refPaths = bindVar.referencePaths || [];
    let removed;

    // try to remove the bind variable in multiple variable declarations
    refPaths.forEach(item => {
        let parentPath = item.getStatementParent();
        if (t.isVariableDeclaration(parentPath) && t.isIdentifier(item)) {
            removeNode(t, item.parentPath, removeOpts);
            removeComments(t, parentPath, 'leadingComments');
            removed = true;
        }
    });

    // if not multiple variable declarations,
    // remove the single variable declaration statement
    if (!removed) {
        removeNode(t, bindVar.path.getStatementParent(), removeOpts);
    }
}

Remove the bind variable declaration

@inner
@param {Object} t the babel type definition
@param {Object} bindVar the bind variable path information
@param {Object} removeOpts the remove options


function getRequiredModulePath(valuePath, moduleName, t, removeRequireDeclaration = false) {
    let bindVar = valuePath.scope.bindings[moduleName];
    if (!bindVar) {
        throw valuePath.buildCodeFrameError(`the variable ${moduleName} import declaration is not found`);
    }

    let declareNodePath = bindVar.path;
    let parentStatementPath = bindVar.path.getStatementParent();

    // check import statement
    if (t.isImportDeclaration(parentStatementPath)) {
        let id = parentStatementPath.node.source.value;
        if (removeRequireDeclaration) {
            let toRemovePath = declareNodePath;
            if (t.isImportDefaultSpecifier(declareNodePath.node)) {
                toRemovePath = declareNodePath.parentPath;
            }
            removeNode(t, toRemovePath, {tail: true});
        }
        return id;
    }
    else if (t.isVariableDeclarator(declareNodePath)) {
        // check require statement
        let initNode = declareNodePath.node.init;
        let id = getRequireExpressionModuleId(initNode, t);
        if (id) {
            removeRequireDeclaration && removeVariableDeclaration(
                t, bindVar, {tail: true}
            );
            return id;
        }
    }

    throw valuePath.buildCodeFrameError(`the variable ${moduleName} import declaration is not found`);
}

function
getRequiredModulePath
(
valuePath
,
moduleName
,
t
,
removeRequireDeclaration
=
false
)
{
let
bindVar
=
valuePath
.
scope
.
bindings
[
moduleName
]
;
if
(
!
bindVar
)
{
throw
valuePath
.
buildCodeFrameError
(
`
${
moduleName
}
`
)
;
}
let
declareNodePath
=
bindVar
.
path
;
let
parentStatementPath
=
bindVar
.
path
.
getStatementParent
(
)
;
// check import statement
if
(
t
.
isImportDeclaration
(
parentStatementPath
)
)
{
let
id
=
parentStatementPath
.
node
.
source
.
value
;
if
(
removeRequireDeclaration
)
{
let
toRemovePath
=
declareNodePath
;
if
(
t
.
isImportDefaultSpecifier
(
declareNodePath
.
node
)
)
{
toRemovePath
=
declareNodePath
.
parentPath
;
}
removeNode
(
t
,
toRemovePath
,
{
tail
:
true
}
)
;
}
return
id
;
}
else
if
(
t
.
isVariableDeclarator
(
declareNodePath
)
)
{
// check require statement
let
initNode
=
declareNodePath
.
node
.
init
;
let
id
=
getRequireExpressionModuleId
(
initNode
,
t
)
;
if
(
id
)
{
removeRequireDeclaration
&&
removeVariableDeclaration
(
t
,
bindVar
,
{
tail
:
true
}
)
;
return
id
;
}
}
throw
valuePath
.
buildCodeFrameError
(
`
${
moduleName
}
`
)
;
}

Get the required module path or id information

@inner
@param {Object} valuePath the used module variable node path
@param {string} moduleName the module variable name
@param {Object} t the babel type definition
@param {boolean=} removeRequireDeclaration whether remove the module require
declaration statement.
@return {string}


function transformFilterInAttribute(element, tplOpts, options) {
    let attrs = element.attribs;
    let filterAttrs = element._hasFilterAttrs;
    if (!filterAttrs || !filterAttrs.length) {
        return;
    }

    let {logger, file} = tplOpts;
    let {filters} = options;
    filterAttrs.forEach(k => {
        let value = attrs[k];
        attrs[k] = updateFilterCall(filters, logger, file, value);
    });
}

function transformFilterInAttribute ( element , tplOpts , options ) { let attrs = element . attribs ; let filterAttrs = element . _hasFilterAttrs ; if ( ! filterAttrs || ! filterAttrs . length ) { return ; } let { logger , file } = tplOpts ; let { filters } = options ; filterAttrs . forEach ( k => { let value = attrs [ k ] ; attrs [ k ] = updateFilterCall ( filters , logger , file , value ) ; } ) ; }

Transform element node filter info

@param {Object} element the element node to transform
@param {Object} tplOpts the template transform options
@param {Object=} options the plugin options, optional


function processFile(file, processor, buildManager) {
    let {compileContext, logger} = buildManager;
    let {handler, options: opts, rext} = processor;
    logger.debug(`compile file ${file.path}, using ${processor.name}: `, opts);

    let result = handler(file, Object.assign({
        config: opts
    }, compileContext));
    if (!result) {
        return;
    }

    rext && (file.rext = rext);

    if (isPromise(result)) {
        buildManager.addAsyncTask(file, result);
        return;
    }

    if (result.isSfcComponent) {
        compileComponent(result, file, buildManager);
        result = {content: file.content};
    }
    buildManager.updateFileCompileResult(file, result);
}

function processFile ( file , processor , buildManager ) { let { compileContext , logger } = buildManager ; let { handler , options : opts , rext } = processor ; logger . debug ( ` ${ file . path } ${ processor . name } ` , opts ) ; let result = handler ( file , Object . assign ( { config : opts } , compileContext ) ) ; if ( ! result ) { return ; } rext && ( file . rext = rext ) ; if ( isPromise ( result ) ) { buildManager . addAsyncTask ( file , result ) ; return ; } if ( result . isSfcComponent ) { compileComponent ( result , file , buildManager ) ; result = { content : file . content } ; } buildManager . updateFileCompileResult ( file , result ) ; }

Process file using the given processor

@inner
@param {Object} file the file to process
@param {Object} processor the processor to use
@param {BuildManager} buildManager the build manager


function getCustomComponentTags(config) {
    let {usingComponents} = config || {};
    if (!usingComponents) {
        return;
    }

    return Object.keys(usingComponents).map(k => toHyphen(k));
}

function getCustomComponentTags ( config ) { let { usingComponents } = config || { } ; if ( ! usingComponents ) { return ; } return Object . keys ( usingComponents ) . map ( k => toHyphen ( k ) ) ; }

Get custom component tags

@inner
@param {Object} config the component config
@return {?Array.<string>}


function getImportFilterModules(usedFilters, scriptFile, buildManager) {
    if (!usedFilters) {
        return;
    }

    let filterModules = [];
    let {file: filterFile, filterNames: definedFilters} = scriptFile.filters || {};
    let hasFilterUsed = definedFilters && usedFilters.some(item => {
        if (definedFilters.includes(item)) {
            return true;
        }
        return false;
    });

    if (!hasFilterUsed) {
        return filterModules;
    }

    if (filterFile) {
        let src = relative(filterFile.path, scriptFile.dirname);
        if (src.charAt(0) !== '.') {
            src = './' + src;
        }
        filterModules.push({src, filters: definedFilters});
    }
    else {
        filterModules.push({filters: definedFilters});
    }

    return filterModules;
}

function getImportFilterModules ( usedFilters , scriptFile , buildManager ) { if ( ! usedFilters ) { return ; } let filterModules = [ ] ; let { file : filterFile , filterNames : definedFilters } = scriptFile . filters || { } ; let hasFilterUsed = definedFilters && usedFilters . some ( item => { if ( definedFilters . includes ( item ) ) { return true ; } return false ; } ) ; if ( ! hasFilterUsed ) { return filterModules ; } if ( filterFile ) { let src = relative ( filterFile . path , scriptFile . dirname ) ; if ( src . charAt ( 0 ) !== '.' ) { src = './' + src ; } filterModules . push ( { src , filters : definedFilters } ) ; } else { filterModules . push ( { filters : definedFilters } ) ; } return filterModules ; }

Get need to import filter modules

@inner
@param {Array.<string>} usedFilters the used filter names
@param {Object} scriptFile the script file to declare the local filter
@param {BuildManager} buildManager the build manager
@return {?Array.<Object>}


function getIncludeTemplateElement(tplFile) {
    let ast = tplFile.ast;
    if (!ast) {
        ast = tplFile.ast = parseDom(tplFile.content.toString());
    }

    let children = ast.children;
    for (let i = 0, len = children.length; i < len; i++) {
        let node = children[i];
        if (node.type === 'tag' && node.name === 'template') {
            return node;
        }
    }
}

function getIncludeTemplateElement ( tplFile ) { let ast = tplFile . ast ; if ( ! ast ) { ast = tplFile . ast = parseDom ( tplFile . content . toString ( ) ) ; } let children = ast . children ; for ( let i = 0 , len = children . length ; i < len ; i ++ ) { let node = children [ i ] ; if ( node . type === 'tag' && node . name === 'template' ) { return node ; } } }

Get included template element

@inner
@param {Object} tplFile the included template file
@return {?Object}


function processAppSpecifiedMediaRule(allAppTypes, appType, rule) {
    let result = matchAppMediaParams(allAppTypes, appType, rule.params);
    if (!result) {
        return;
    }

    let {removed, params} = result;
    params && (params = params.trim());
    if (removed) {
         // remove media rule, not current app media query target
        rule.remove();
    }
    else if (params) {
        // remove current app media query params
        rule.params = params;
    }
    else {
        // remove current app media query rule wrapping
        let children = rule.parent.nodes;
        let currRuleIdx = children.indexOf(rule);

        rule.nodes.forEach(item => {
            item.parent = rule.parent; // up parent

            let itemRaws = item.raws;
            let subNodes = item.nodes;

            // up raw style format
            subNodes && subNodes.forEach(
                sub => sub.raws.before = itemRaws.before
            );
            itemRaws.before = '\n';
            itemRaws.after = '\n';
        });

        children.splice(currRuleIdx, 1, ...rule.nodes);
        rule.nodes = null;
        rule.parent = null;
    }
}

Process media rule.
Remove not current app media query target rule.
Remove current app media query params.

@inner
@param {Array.<string>} allAppTypes all supported build media target
@param {string} appType the current build media target
@param {Object} rule the media query rule

Process media rule . Remove not current app media query target rule . Remove current app media query params .


function removeUnUseDecl(decl, toRemovePropName) {
    let nodes = decl.parent.nodes;
    let currIdx = nodes.indexOf(decl);

    for (let i = currIdx - 1; i >= 0; i--) {
        let item = nodes[i];
        if (item.type === 'decl' && item.prop === toRemovePropName) {
            item.remove();
        }
    }
}

function removeUnUseDecl ( decl , toRemovePropName ) { let nodes = decl . parent . nodes ; let currIdx = nodes . indexOf ( decl ) ; for ( let i = currIdx - 1 ; i >= 0 ; i -- ) { let item = nodes [ i ] ; if ( item . type === 'decl' && item . prop === toRemovePropName ) { item . remove ( ) ; } } }

Remove the given property name style declaration that the position is
front of the current given style declaration.

@inner
@param {Object} decl the style property declaration that will override
the front of the style declaration that has the same property name
@param {string} toRemovePropName the property name to remove

Remove the given property name style declaration that the position is front of the current given style declaration .


function processAppSpecifiedDeclaration(allAppTypes, appType, decl) {
    let {prop, parent} = decl;
    let result;
    if ((result = SPECIFIED_APP_PROP_DECL_REGEXP.exec(prop))) {
        let propApp = result[1];
        let isMatchApp = appType === propApp;
        if (allAppTypes.includes(propApp) && !isMatchApp) {
            // remove not current app build type style declaration
            decl.remove();
        }
        else if (isMatchApp) {
            // remove the previous property style declaration that has same
            // style property name declaration that ignore app type prefix
            // and remove the specified app type prefix of the property
            let newPropName = prop.replace(SPECIFIED_APP_PROP_DECL_REGEXP, '');
            removeUnUseDecl(decl, newPropName);
            decl.prop = newPropName;
        }

        // remove empty rule
        if (!parent.nodes || !parent.nodes.length) {
            parent.remove();
        }
    }
}

Process specified app target css style property declaration

@inner
@param {Array.<string>} allAppTypes all supported build media target
@param {string} appType the current build media target
@param {Object} decl css style declaration


function doMixin(target, source, k, opts) {
    let {
        mixinAttrName = MIXIN_ATTR_NAME,
        lifecycleHookMap,
        mixinStrategy
    } = opts;

    if (k === mixinAttrName) {
        return;
    }

    let mixinHandler = mixinStrategy && mixinStrategy[k];
    if (mixinHandler) {
        target[k] = mixinHandler(target[k], source[k]);
    }
    else if (lifecycleHookMap[k]) {
        let child = target[k];
        let parent = source[k];

        if (!Array.isArray(child)) {
            child = child ? [child] : [];
        }

        if (!Array.isArray(parent)) {
            parent = parent ? [parent] : [];
        }

        child.unshift.apply(child, parent);
        target[k] = child;
    }
    else if (!target.hasOwnProperty(k)) {
        target[k] = source[k];
    }
}

function doMixin ( target , source , k , opts ) { let { mixinAttrName = MIXIN_ATTR_NAME , lifecycleHookMap , mixinStrategy } = opts ; if ( k === mixinAttrName ) { return ; } let mixinHandler = mixinStrategy && mixinStrategy [ k ] ; if ( mixinHandler ) { target [ k ] = mixinHandler ( target [ k ] , source [ k ] ) ; } else if ( lifecycleHookMap [ k ] ) { let child = target [ k ] ; let parent = source [ k ] ; if ( ! Array . isArray ( child ) ) { child = child ? [ child ] : [ ] ; } if ( ! Array . isArray ( parent ) ) { parent = parent ? [ parent ] : [ ] ; } child . unshift . apply ( child , parent ) ; target [ k ] = child ; } else if ( ! target . hasOwnProperty ( k ) ) { target [ k ] = source [ k ] ; } }

Mixin the target with the source

@inner
@param {Object} target the target
@param {Object} source the source
@param {string} k the key
@param {Object} opts the mixin options


function flattenMixins(mixins, target, opts) {
    mixins.forEach(item => {
        if (typeof item === 'function') {
            item = item(true, opts); // return okam mixin definition
        }

        if (!item || typeof item !== 'object') {
            return;
        }

        let submixins = item.mixins;
        if (Array.isArray(submixins) && submixins.length) {
            item = Object.assign({}, item);
            flattenMixins(submixins, item, opts);
        }

        Object.keys(item).forEach(
            k => doMixin(target, item, k, opts)
        );
    });
}

function
flattenMixins
(
mixins
,
target
,
opts
)
{
mixins
.
forEach
(
item
=>
{
if
(
typeof
item
===
'function'
)
{
item
=
item
(
true
,
opts
)
;
// return okam mixin definition
}
if
(
!
item
||
typeof
item
!==
'object'
)
{
return
;
}
let
submixins
=
item
.
mixins
;
if
(
Array
.
isArray
(
submixins
)
&&
submixins
.
length
)
{
item
=
Object
.
assign
(
{
}
,
item
)
;
flattenMixins
(
submixins
,
item
,
opts
)
;
}
Object
.
keys
(
item
)
.
forEach
(
k
=>
doMixin
(
target
,
item
,
k
,
opts
)
)
;
}
)
;
}

Flatten mixins for not support native behavior or Page mixins

@inner
@param {Array.<string|Object>} mixins the mixins
@param {Object} target the target component or behavior
@param {Object=} opts the mixin options


function addBuiltinPlugin(pluginName, appType, plugins, insertAtTop) {
    let pluginInfo = BUILTIN_PLUGINS[pluginName];
    if (typeof pluginInfo === 'object') {
        pluginInfo = pluginInfo[appType] || pluginInfo.default;
    }
    pluginInfo = normalizeViewPlugins([pluginInfo], appType)[0];

    let plugin = Array.isArray(pluginInfo) ? pluginInfo[0] : pluginInfo;
    let hasBuiltinPlugin = plugins.some(
        item => (plugin === (Array.isArray(item) ? item[0] : item))
    );
    if (!hasBuiltinPlugin) {
        plugins[insertAtTop ? 'unshift' : 'push'](pluginInfo);
    }
}

function addBuiltinPlugin ( pluginName , appType , plugins , insertAtTop ) { let pluginInfo = BUILTIN_PLUGINS [ pluginName ] ; if ( typeof pluginInfo === 'object' ) { pluginInfo = pluginInfo [ appType ] || pluginInfo . default ; } pluginInfo = normalizeViewPlugins ( [ pluginInfo ] , appType ) [ 0 ] ; let plugin = Array . isArray ( pluginInfo ) ? pluginInfo [ 0 ] : pluginInfo ; let hasBuiltinPlugin = plugins . some ( item => ( plugin === ( Array . isArray ( item ) ? item [ 0 ] : item ) ) ) ; if ( ! hasBuiltinPlugin ) { plugins [ insertAtTop ? 'unshift' : 'push' ] ( pluginInfo ) ; } }

Add ref plugin

@inner
@param {string} pluginName the builtin plugin name to add
@param {string} appType the app type to transform
@param {Array.<Object>} plugins the existed plugins
@param {boolean=} insertAtTop whether insert the added plugin at the first position


function normalizeViewPlugins(plugins, appType) {
    return plugins.map(pluginInfo => {
        let pluginItem = pluginInfo;
        let pluginOptions;
        if (Array.isArray(pluginInfo)) {
            pluginItem = pluginInfo[0];
            pluginOptions = pluginInfo[1];
        }

        if (typeof pluginItem === 'string') {
            let pluginPath = BUILTIN_PLUGINS[pluginItem];
            if (typeof pluginPath === 'function') {
                pluginPath = pluginPath(appType);
            }
            else if (pluginPath && typeof pluginPath === 'object') {
                pluginPath = pluginPath[appType] || pluginPath.default;
            }

            if (pluginPath && Array.isArray(pluginPath)) {
                pluginOptions = pluginPath[1];
                pluginPath = pluginPath[0];
            }

            pluginPath && (pluginItem = pluginPath);
        }

        if (typeof pluginItem === 'string') {
            pluginItem = require(pluginItem);
        }

        return pluginOptions ? [pluginItem, pluginOptions] : pluginItem;
    });
}

function normalizeViewPlugins ( plugins , appType ) { return plugins . map ( pluginInfo => { let pluginItem = pluginInfo ; let pluginOptions ; if ( Array . isArray ( pluginInfo ) ) { pluginItem = pluginInfo [ 0 ] ; pluginOptions = pluginInfo [ 1 ] ; } if ( typeof pluginItem === 'string' ) { let pluginPath = BUILTIN_PLUGINS [ pluginItem ] ; if ( typeof pluginPath === 'function' ) { pluginPath = pluginPath ( appType ) ; } else if ( pluginPath && typeof pluginPath === 'object' ) { pluginPath = pluginPath [ appType ] || pluginPath . default ; } if ( pluginPath && Array . isArray ( pluginPath ) ) { pluginOptions = pluginPath [ 1 ] ; pluginPath = pluginPath [ 0 ] ; } pluginPath && ( pluginItem = pluginPath ) ; } if ( typeof pluginItem === 'string' ) { pluginItem = require ( pluginItem ) ; } return pluginOptions ? [ pluginItem , pluginOptions ] : pluginItem ; } ) ; }

Normalize the view transformation plugins

@inner
@param {Array.<string|Object>} plugins the plugins to normalize
@param {string} appType the appType to transform
@return {Array.<Object>}


function handleOnTag(file, tagName, replaceTagName) {
    let tags = file.tags;
    tags || (tags = file.tags = {});
    if (replaceTagName && tags.hasOwnProperty(replaceTagName)) {
        delete tags[replaceTagName];
    }

    tags[tagName] = true;
}

function handleOnTag ( file , tagName , replaceTagName ) { let tags = file . tags ; tags || ( tags = file . tags = { } ) ; if ( replaceTagName && tags . hasOwnProperty ( replaceTagName ) ) { delete tags [ replaceTagName ] ; } tags [ tagName ] = true ; }

On tag handler

@inner
@param {Object} file the template file
@param {string} tagName the tag name to encounter
@param {string=} replaceTagName the old tag name to replace


function handleOnFilter(file, filterName) {
    let usedFilters = file.filters;
    usedFilters || (usedFilters = file.filters = []);
    if (!usedFilters.includes(filterName)) {
        usedFilters.push(filterName);
    }
}

function handleOnFilter ( file , filterName ) { let usedFilters = file . filters ; usedFilters || ( usedFilters = file . filters = [ ] ) ; if ( ! usedFilters . includes ( filterName ) ) { usedFilters . push ( filterName ) ; } }

On filter handler

@inner
@param {Object} file the template file
@param {string} filterName the filter name


function initViewTransformOptions(file, processOpts, buildManager, isNativeView) {
    let plugins = processOpts.plugins;
    let {appType, componentConf, buildConf} = buildManager;
    if (isNativeView) {
        return Object.assign({}, processOpts, {
            plugins: normalizeViewPlugins(plugins, appType)
        });
    }

    let templateConf = (componentConf && componentConf.template) || {};
    if (!plugins || !plugins.length) {
        plugins = ['syntax'];

        if (templateConf.transformTags) {
            plugins.push('tagTransform');
        }

        // vuePrefix  需要在第一位，v- directives 处理成 directives 再处理
        if (templateConf.useVuePrefix) {
            plugins.unshift('vuePrefix');
        }
    }

    plugins = normalizeViewPlugins(plugins, appType);
    let isSupportRef = buildManager.isEnableRefSupport();
    isSupportRef && addBuiltinPlugin('ref', appType, plugins);

    let isSupportVHtml = buildManager.isEnableVHtmlSupport();
    isSupportVHtml && addBuiltinPlugin('vhtml', appType, plugins);

    addBuiltinPlugin('resource', appType, plugins, true);

    processOpts.plugins = plugins;

    let filterOptions = buildManager.getFilterTransformOptions();
    if (filterOptions) {
        filterOptions = Object.assign({
            onFilter: handleOnFilter.bind(null, file)
        }, filterOptions);
    }

    return Object.assign(
        {},
        processOpts,
        {
            framework: buildConf.framework || [],
            plugins,
            filter: filterOptions,
            template: templateConf,
            onTag: handleOnTag.bind(null, file)
        }
    );
}

Initialize component view template transform options.

@param {Object} file the file to process
@param {Object} processOpts the process options
@param {Array.<string|Object>} processOpts.plugins the view processor plugins,
the builtin plugins:
`syntax`: transform okam template syntax to mini program template syntax
`tagTransform`: transform tags A to tag B
`ref`: provide view `ref` attribute support like Vue
You can also pass your custom plugin:
{
tag() {} // refer to the ref plugin implementation
}
@param {BuildManager} buildManager the build manager
@param {boolean=} isNativeView whether is native view transformation
@return {Object}

Initialize component view template transform options .


function navigateTo(options) {
    let {url, params} = options;
    let queryIdx = url.indexOf('?');
    if (queryIdx !== -1) {
        let query = url.substr(queryIdx + 1);
        url = url.substring(0, queryIdx);

        if (query) {
            query = parseQuery(query);
            params = Object.assign({}, query, params);
        }
    }

    if (url.charAt(0) === '/') {
        let urlParts = url.split('/');
        urlParts.pop(); // remove component name
        url = urlParts.join('/');
    }

    router.push({
        uri: url,
        params
    });
}

function
navigateTo
(
options
)
{
let
{
url
,
params
}
=
options
;
let
queryIdx
=
url
.
indexOf
(
'?'
)
;
if
(
queryIdx
!==
1
)
{
let
query
=
url
.
substr
(
queryIdx
+
1
)
;
url
=
url
.
substring
(
0
,
queryIdx
)
;
if
(
query
)
{
query
=
parseQuery
(
query
)
;
params
=
Object
.
assign
(
{
}
,
query
,
params
)
;
}
}
if
(
url
.
charAt
(
0
)
===
'/'
)
{
let
urlParts
=
url
.
split
(
'/'
)
;
urlParts
.
pop
(
)
;
// remove component name
url
=
urlParts
.
join
(
'/'
)
;
}
router
.
push
(
{
uri
:
url
,
params
}
)
;
}

Navigate to the specified url

@param {Object} options the navigation options
@param {string} options.url the url to navigate to
@param {Object=} options.params the navigation params, which is not
supported in weixin env


function compile(file, options) {
    let config = options.config || {};
    let presets = config.presets || [];

    let tsPreset = '@babel/preset-typescript';
    if (!presets.includes(tsPreset)) {
        config.presets = [tsPreset].concat(presets);
        options.config = config;
    }

    return parserHelper.compile(file, options, 7);
}

function compile ( file , options ) { let config = options . config || { } ; let presets = config . presets || [ ] ; let tsPreset = '@babel/preset-typescript' ; if ( ! presets . includes ( tsPreset ) ) { config . presets = [ tsPreset ] . concat ( presets ) ; options . config = config ; } return parserHelper . compile ( file , options , 7 ) ; }

Compile the file using babel

@param {Object} file the file to process
@param {Object} options the compile options
@return {{content: string, sourceMap: string}}


function createInitCallArgs(declarationPath, config, opts, t) {
    let {isPage, isComponent, isApp, getInitOptions} = opts;
    let callArgs = [declarationPath.node];
    let initOptions;
    if (opts.tplRefs) {
        initOptions = {refs: opts.tplRefs};
    }

    let extraInitOpts = getInitOptions && getInitOptions(
        config, {isPage, isComponent, isApp}
    );
    if (extraInitOpts) {
        initOptions || (initOptions = {});
        Object.assign(initOptions, extraInitOpts);
    }

    if (initOptions) {
        // insert the `ref` information defined in template to the constructor
        callArgs.push(
            createSimpleObjectExpression(
                initOptions, t
            )
        );
    }

    return callArgs;
}

Create init call arguments

@inner
@param {Object} declarationPath the declaration statement path to process
@param {Object} config the config property value info defined in module
@param {Object} opts the transformation options
@param {Object} t the babel type definition
@return {Array}


function transformMiniProgram(t, path, declarationPath, config, opts) {
    if (t.isObjectExpression(declarationPath)) {
        // extract the config information defined in the code
        declarationPath.traverse(
            getCodeTraverseVisitors(t, config, opts)
        );
    }
    else if (opts.isExtension) {
        return;
    }
    else {
        throw path.buildCodeFrameError('export require plain object');
    }

    let baseClassName = path.scope.generateUid(opts.baseName);
    let rootPath = path.findParent(p => t.isProgram(p));
    let bodyPath = rootPath.get('body.0');

    // ensure the inserted import declaration is after the leading comment
    removeComments(t, bodyPath, 'leadingComments');

    // insert the base name import statement
    bodyPath.insertBefore(
        createImportDeclaration(baseClassName, opts.baseId, t)
    );

    if (opts.isApp) {
        // insert the app extension use statements
        appTransformer.extendAppFramework(
            t, path, bodyPath, baseClassName, opts
        );
    }

    let callArgs = createInitCallArgs(declarationPath, config.config, opts, t);
    let needExport = opts.needExport || !opts.baseClass;
    let toReplacePath = needExport
        ? path.get('declaration')
        : path;
    // wrap the export module using the base name
    let callExpression = t.callExpression(
        t.identifier(baseClassName),
        callArgs
    );
    if (opts.isBehavior || !opts.baseClass) {
        toReplacePath.replaceWith(t.expressionStatement(
            callExpression
        ));
    }
    else {
        toReplacePath.replaceWith(
            t.expressionStatement(
                t.callExpression(
                    t.identifier(opts.baseClass),
                    [
                        callExpression
                    ]
                )
            )
        );
    }

    // stop traverse to avoid the new created export default statement above
    // that be revisited in this plugin `ExportDefaultDeclaration` visitor
    // path.stop();
}

Transform mini program code

@inner
@param {Object} t the babel type definition
@param {Object} path the node path to transform
@param {Object} declarationPath the declaration statement path to process
@param {Object} config the config used to cache the config information
defined in the code
@param {Object} opts the transformation options


function getTransformOptions(options, state) {
    let transformOpts = Object.assign({}, options, state.opts);
    transformOpts.baseId = options.extensionName
        ? getFrameworkExtendId(transformOpts.appType, options.extensionName, true)
        : getBaseId(transformOpts.appType, transformOpts.baseName);
    return transformOpts;
}

function getTransformOptions ( options , state ) { let transformOpts = Object . assign ( { } , options , state . opts ) ; transformOpts . baseId = options . extensionName ? getFrameworkExtendId ( transformOpts . appType , options . extensionName , true ) : getBaseId ( transformOpts . appType , transformOpts . baseName ) ; return transformOpts ; }

Get transform options

@inner
@param {Object} options the transformation type options
@param {Object} state the transformation plugin state
@return {Object}


function getPageInfo(pagePath, prefix) {
    let parts = pagePath.split('/');
    let componentName = parts.pop();
    let pageName = parts.join('/');

    prefix && (pageName = `${prefix}/${pageName}`);
    return {
        pageName,
        componentName
    };
}

function getPageInfo ( pagePath , prefix ) { let parts = pagePath . split ( '/' ) ; let componentName = parts . pop ( ) ; let pageName = parts . join ( '/' ) ; prefix && ( pageName = ` ${ prefix } ${ pageName } ` ) ; return { pageName , componentName } ; }

Get the router page information

@inner
@param {string} pagePath the page path
@param {string=} prefix the page name prefix, optional
@return {{pageName: string, componentName: string}}


function normalizePages(result, pages, prefix) {
    let entryPageName;
    pages.forEach((item, idx) => {
        let filter;
        if (typeof item === 'object') {
            let {path, filter: pageFilter} = item;
            item = path;
            filter = pageFilter;
        }

        let {pageName, componentName} = getPageInfo(item, prefix);
        let pageInfo = {
            component: componentName
        };
        filter && (pageInfo.filter = filter);
        result[pageName] = pageInfo;
        if (idx === 0) {
            entryPageName = pageName;
        }
    });

    return entryPageName;
}

function normalizePages ( result , pages , prefix ) { let entryPageName ; pages . forEach ( ( item , idx ) => { let filter ; if ( typeof item === 'object' ) { let { path , filter : pageFilter } = item ; item = path ; filter = pageFilter ; } let { pageName , componentName } = getPageInfo ( item , prefix ) ; let pageInfo = { component : componentName } ; filter && ( pageInfo . filter = filter ) ; result [ pageName ] = pageInfo ; if ( idx === 0 ) { entryPageName = pageName ; } } ) ; return entryPageName ; }

Normalize router page information

@inner
@param {Object} result the existed router pages
@param {Array} pages the router pages to normalize
@param {string=} prefix the page name prefix, optional
@return {string} return the entry page name


function normalizeRouteInfo(pages, subPackages) {
    let result = {};
    let entryPageName = normalizePages(result, pages);

    subPackages && subPackages.forEach(item => {
        let {root, pages: subPages} = item;
        normalizePages(result, subPages, root);
    });

    return {
        entry: entryPageName,
        pages: result
    };
}

function normalizeRouteInfo ( pages , subPackages ) { let result = { } ; let entryPageName = normalizePages ( result , pages ) ; subPackages && subPackages . forEach ( item => { let { root , pages : subPages } = item ; normalizePages ( result , subPages , root ) ; } ) ; return { entry : entryPageName , pages : result } ; }

Normalize app route info

@inner
@param {Array.<string>} pages the page path list
@param {Array.<Object>=} subPackages the sub packages, optional
@return {Object}


function addDisplayPageConfig(pages, sourceDir, configFile) {
    let path = relative(configFile.fullPath, sourceDir);
    let {pageName} = getPageInfo(path);
    let currPageDisplayInfo = pages[pageName];
    let pageConfig = JSON.parse(configFile.content);

    let result = {};
    // filter the illegal not supported config item
    // and map the weixin config name to quick app
    Object.keys(pageConfig).forEach(k => {
        let newKey = DISPLAY_PAGE_CONFIG_MAP[k];
        if (newKey) {
            result[newKey] = pageConfig[k];
        }
    });

    // merge the page config, the config defined in page has higher priority
    // than the app config
    pages[pageName] = Object.assign({}, currPageDisplayInfo, result);
}

Add display page config info based the config defined in page

@inner
@param {Object} pages the existed display page config
@param {string} sourceDir the project source directory
@param {Object} configFile the config file


function normalizeWindowConfig(windowConfig) {
    if (!windowConfig) {
        return;
    }

    let result = {};
    // filter the illegal not supported config item
    // and map the weixin config name to quick app
    Object.keys(windowConfig).forEach(k => {
        let newKey = DISPLAY_PAGE_CONFIG_MAP[k];
        if (newKey) {
            result[newKey] = windowConfig[k];
        }
    });
    return result;
}

Normalize window config, convert it as quick app display config

@inner
@param {?Object} windowConfig the window config
@return {?Object}

Normalize window config convert it as quick app display config


function addFeatureItem(logger, existed, name, params) {
    if (!existed[name]) {
        existed[name] = true;
        let feature = {name};
        params && (feature.params = params);
        return feature;
    }

    logger.debug('duplicated feature declaration', name);
    return false;
}

function
addFeatureItem
(
logger
,
existed
,
name
,
params
)
{
if
(
!
existed
[
name
]
)
{
existed
[
name
]
=
true
;
let
feature
=
{
name
}
;
params
&&
(
feature
.
params
=
params
)
;
return
feature
;
}
logger
.
debug
(
'duplicated feature declaration'
,
name
)
;
return
false
;
}

Add API feature item.
Return the feature item to add, or return false if existed.

@inner
@param {Object} logger logger the logger utilities
@param {Object} existed the added feature item map
@param {string} name the feature item name to add
@param {Object=} params the feature item params, optional
@return {Object|boolean}

Add API feature item . Return the feature item to add or return false if existed .


function normalizeFeatureItem(item, existed, result, logger) {
    let addItem;
    if (typeof item === 'string') {
        addItem = addFeatureItem(logger, existed, item);
    }
    else if (Array.isArray(item)) {
        let [name, params] = item;
        addItem = addFeatureItem(logger, existed, name, params);
    }
    else if (item && typeof item === 'object') {
        let {name, params} = item;
        addItem = addFeatureItem(logger, existed, name, params);
    }

    if (addItem) {
        result.push(addItem);
    }
}

function normalizeFeatureItem ( item , existed , result , logger ) { let addItem ; if ( typeof item === 'string' ) { addItem = addFeatureItem ( logger , existed , item ) ; } else if ( Array . isArray ( item ) ) { let [ name , params ] = item ; addItem = addFeatureItem ( logger , existed , name , params ) ; } else if ( item && typeof item === 'object' ) { let { name , params } = item ; addItem = addFeatureItem ( logger , existed , name , params ) ; } if ( addItem ) { result . push ( addItem ) ; } }

Normalize API feature item

@inner
@param {*} item the feature item to normalize
@param {Object} existed the added feature item map
@param {Array.<Object>} result the normalized feature items result
@param {Object} logger logger the logger utilities


function compile(file, options) {
    if (!file.compileReady && !file.owner.processed) {
        return {content: file.content};
    }

    let {
        getAllPageConfigFiles,
        sourceDir,
        designWidth
    } = options;
    let obj = JSON.parse(file.content.toString());

    // normalize router info
    let routerInfo = normalizeRouteInfo(obj.pages);
    delete obj.pages;
    obj.router = routerInfo;

    // normalize debug info
    normalizeDebugConfig(obj);

    // init designWidth
    if (designWidth) {
        obj.config || (obj.config = {});
        if (!obj.config.designWidth) {
            obj.config.designWidth = designWidth;
        }
    }

    // normalize window config info
    let displayInfo = obj.display;
    let displayConfig = normalizeWindowConfig(obj.window);
    // the display config has higher priority than window config
    displayConfig && (displayInfo = Object.assign(displayConfig, displayInfo));
    displayInfo && (obj.display = displayInfo);
    delete obj.window;

    // normalize display pages info
    if (displayInfo && displayInfo.pages) {
        displayInfo.pages = normalizeDisplayPages(displayInfo.pages);
    }

    // normalize features
    let features = getUsedAPIFeatures(obj.features, options);
    features && (obj.features = features);

    // merge the page display config defined in page
    let pageConfigFiles = getAllPageConfigFiles();
    let currDisplayPages = (displayInfo && displayInfo.pages) || {};
    pageConfigFiles.forEach(item => {
        addDisplayPageConfig(currDisplayPages, sourceDir, item);
    });
    if (Object.keys(currDisplayPages).length) {
        displayInfo || (obj.display = {});
        obj.display.pages = currDisplayPages;
    }

    return {
        content: JSON.stringify(obj, null, 4)
    };
}

Compile quick app json, it should compile after all files build done

@param {Object} file the file to process
@param {Object} options the compile options
@return {{content: string}}

Compile quick app json it should compile after all files build done


function normalizeQuickAppPage(pageInfo) {
    pageInfo = normalizePage(pageInfo);

    let {data, dataAccessType} = pageInfo;
    if (data && !pageInfo[dataAccessType]) {
        pageInfo[dataAccessType] = data;
        delete pageInfo.data;
    }

    // extract the protected and public data props as the query props
    let queryProps = [];
    if (pageInfo.protected) {
        queryProps = Object.keys(pageInfo.protected);
    }
    if (pageInfo.public) {
        queryProps = queryProps.push.apply(
            queryProps, Object.keys(pageInfo.public)
        );
    }

    let privateData = pageInfo.private;
    privateData || (privateData = pageInfo.private = {});
    // we using `__` suffix as the inner data, as for `_` leading char is not allowed
    // hook the data to the page context is also not allowed, so we need put it
    // to the privateData
    /* eslint-disable fecs-camelcase */
    privateData.queryProps__ = queryProps;

    return pageInfo;
}

Normalize the page information

@inner
@param {Object} pageInfo the page information to normalize
@return {Object}


function getImportTemplateElement(tplFile, name) {
    if (!name) {
        return;
    }

    let ast = tplFile.ast;
    if (!ast) {
        ast = tplFile.ast = parseDom(tplFile.content.toString());
    }

    let children = ast.children;
    for (let i = 0, len = children.length; i < len; i++) {
        let node = children[i];
        if (node.type === 'tag' && node.name === 'template') {
            let {attribs: attrs} = node;
            if (attrs && attrs.name === name) {
                return node;
            }
        }
    }
}

function getImportTemplateElement ( tplFile , name ) { if ( ! name ) { return ; } let ast = tplFile . ast ; if ( ! ast ) { ast = tplFile . ast = parseDom ( tplFile . content . toString ( ) ) ; } let children = ast . children ; for ( let i = 0 , len = children . length ; i < len ; i ++ ) { let node = children [ i ] ; if ( node . type === 'tag' && node . name === 'template' ) { let { attribs : attrs } = node ; if ( attrs && attrs . name === name ) { return node ; } } } }

Get the imported template element based on the given template name.

@inner
@param {Object} tplFile the imported template file
@param {string} name the template name to use
@return {?Object}

Get the imported template element based on the given template name .


function parseTplDataInfo(value) {
    value = value.substring(1, value.length - 1);
    let items = value.split(',');
    let dataObjAttrs = {};
    items.forEach(item => {
        let colonIdx = item.indexOf(':');
        if (colonIdx !== -1) {
            let key = item.substring(0, colonIdx).trim();
            let value = item.substr(colonIdx + 1).trim();

            key === value || (dataObjAttrs[key] = value);
        }
    });

    return Object.keys(dataObjAttrs).length ? dataObjAttrs : null;
}

function parseTplDataInfo ( value ) { value = value . substring ( 1 , value . length - 1 ) ; let items = value . split ( ',' ) ; let dataObjAttrs = { } ; items . forEach ( item => { let colonIdx = item . indexOf ( ':' ) ; if ( colonIdx !== - 1 ) { let key = item . substring ( 0 , colonIdx ) . trim ( ) ; let value = item . substr ( colonIdx + 1 ) . trim ( ) ; key === value || ( dataObjAttrs [ key ] = value ) ; } } ) ; return Object . keys ( dataObjAttrs ) . length ? dataObjAttrs : null ; }

Parse the used data information of the import template. If the data attribute
name is not changed, it'll ignore.

e.g.,
input: a:b, c, ...obj
output: {a: b}

@inner
@param {string} value the data value
@return {?Object}

Parse the used data information of the import template . If the data attribute name is not changed it ll ignore .


function replaceTemplateVariable(value, data) {
    return value.replace(/\{\{(.+)\}\}/g, (match, varName) => {
        varName = varName.trim();
        let newVarName = data[varName];
        if (newVarName) {
            return `{{${newVarName}}}`;
        }
        return match;
    });
}

function replaceTemplateVariable ( value , data ) { return value . replace ( / \{\{(.+)\}\} / g , ( match , varName ) => { varName = varName . trim ( ) ; let newVarName = data [ varName ] ; if ( newVarName ) { return ` ${ newVarName } ` ; } return match ; } ) ; }

Replace template variable using the new data variable name

@inner
@param {string} value the value to update
@param {Object} data the data variable name map, key is the old variable name,
value is the new variable name
@return {string} return the updated value


function replaceElementAttributeVariable(attrs, data) {
    let newAttrs = Object.assign({}, attrs);

    attrs && Object.keys(attrs).forEach(k => {
        let value = attrs[k];
        if (k.startsWith(':')) {
            // replace okam data variable
            let newDataName = data[value];
            newDataName && (newAttrs[k] = newDataName);
        }
        else {
            value = replaceTemplateVariable(value, data);
            newAttrs[k] = value;
        }
    });
    return newAttrs;
}

function
replaceElementAttributeVariable
(
attrs
,
data
)
{
let
newAttrs
=
Object
.
assign
(
{
}
,
attrs
)
;
attrs
&&
Object
.
keys
(
attrs
)
.
forEach
(
k
=>
{
let
value
=
attrs
[
k
]
;
if
(
k
.
startsWith
(
':'
)
)
{
// replace okam data variable
let
newDataName
=
data
[
value
]
;
newDataName
&&
(
newAttrs
[
k
]
=
newDataName
)
;
}
else
{
value
=
replaceTemplateVariable
(
value
,
data
)
;
newAttrs
[
k
]
=
value
;
}
}
)
;
return
newAttrs
;
}

Replace element attribute bind data variable using the new data variable name

@inner
@param {Object} attrs the attributes to update
@param {Object} data the data variable name map, key is the old variable name,
value is the new variable name
@return {Array} return the updated attributes


function updateTemplateDataVariableName(element, data) {
    let {children} = element;
    let result = [];
    children && children.forEach(item => {
        let {type} = item.type;
        let newItem = Object.assign({}, item);

        if (type === 'tag') {
            let {attribs: attrs, children} = item;
            newItem.attribs = replaceElementAttributeVariable(attrs, data);

            if (children) {
                let newChildren = updateTemplateDataVariableName(item, data);
                newChildren && (newItem.children = newChildren);
            }
        }
        else if (type === 'text') {
            let {data: textValue} = item;
            textValue = replaceTemplateVariable(textValue, data);
            newItem.data = textValue;
        }

        result.push(newItem);
    });
    return result;
}

function updateTemplateDataVariableName ( element , data ) { let { children } = element ; let result = [ ] ; children && children . forEach ( item => { let { type } = item . type ; let newItem = Object . assign ( { } , item ) ; if ( type === 'tag' ) { let { attribs : attrs , children } = item ; newItem . attribs = replaceElementAttributeVariable ( attrs , data ) ; if ( children ) { let newChildren = updateTemplateDataVariableName ( item , data ) ; newChildren && ( newItem . children = newChildren ) ; } } else if ( type === 'text' ) { let { data : textValue } = item ; textValue = replaceTemplateVariable ( textValue , data ) ; newItem . data = textValue ; } result . push ( newItem ) ; } ) ; return result ; }

Update the inline template data variable name

@inner
@param {Object} element the element to update
@param {Object} data the data variable name map, key is the old variable name,
value is the new variable name
@return {Array} return the updated element nodes


function shouldPolyfill(importName, name, path) {
    let result;
    if (Array.isArray(importName)) {
        result = importName.some(item => (item === name));
    }
    else {
        result = importName === name;
    }

    // ignore global variable, e.g., Array/Number/Promise etc.
    return result && !path.scope.hasBinding(name, true);
}

Check given the variable name whether need to polyfill

@inner
@param {string} importName the variable name to polyfill
@param {string} name current variable name
@param {Object} path the node path
@return {boolean}


function proxyDataGetter(ctx, prop) {
    let proxyProps = ctx.__proxyProps;
    proxyProps || (proxyProps = ctx.__proxyProps = {});
    if (proxyProps[prop]) {
        return;
    }

    proxyProps[prop] = true;

    let descriptor = Object.getOwnPropertyDescriptor(ctx, prop);
    if (descriptor && descriptor.configurable) {
        let newDescriptor = Object.assign({}, descriptor, {
            get() {
                ctx.__deps && ctx.__deps.push(prop);
                return descriptor.get && descriptor.get.call(ctx);
            }
        });
        Object.defineProperty(ctx, prop, newDescriptor);
    }
    else {
        console.warn('cannot configure the data prop descriptor info:', prop);
    }
}

Proxy data getter

@inner
@param {Object} ctx the component instance
@param {string} prop the property name


function addDataChangeWatcher(ctx, prop, deep) {
    // in quick app, the watch handler does not support dynamic added methods
    // let handler = '__handleDataChange$' + prop;
    // ctx[handler] = (newVal, oldVal) => ctx.__handleDataChange(
    //     prop, newVal, oldVal
    // );

    // FIXME: array cannot support deep watch in quick app when change array item info
    let handlerName = getInnerWatcher(prop);
    watchDataChange.call(ctx, prop, handlerName, {deep});
}

Add data change watcher

@inner
@param {Object} ctx the component instance
@param {string} prop the data prop name
@param {boolean=} deep whether watch deep


function collectComputedPropDeps(ctx, prop, getter) {
    ctx.__deps = [];
    let value = getter.call(ctx);
    ctx.__computedDeps[prop] = ctx.__deps;
    ctx.__deps = null;

    return value;
}

function collectComputedPropDeps ( ctx , prop , getter ) { ctx . __deps = [ ] ; let value = getter . call ( ctx ) ; ctx . __computedDeps [ prop ] = ctx . __deps ; ctx . __deps = null ; return value ; }

Collect the computed prop dependence data props

@inner
@param {Object} ctx the component instance
@param {string} prop the computed prop name
@param {Function} getter the computed getter
@return {*}


function findChangeComputedProps(allDeps, changeProp) {
    let result = [];
    Object.keys(allDeps).forEach(k => {
        let depList = allDeps[k];
        if (k !== changeProp && depList.indexOf(changeProp) !== -1) {
            result.push(k);
        }
    });
    return result;
}

function findChangeComputedProps ( allDeps , changeProp ) { let result = [ ] ; Object . keys ( allDeps ) . forEach ( k => { let depList = allDeps [ k ] ; if ( k !== changeProp && depList . indexOf ( changeProp ) !== - 1 ) { result . push ( k ) ; } } ) ; return result ; }

Find the changed computed props

@inner
@param {Object} allDeps all computed props deps info
@param {string} changeProp the changed prop name
@return {Array.<string>}


function initGlobalComponents(appType, componentConf, sourceDir) {
    let {global: globalComponents} = componentConf;
    if (!globalComponents) {
        return;
    }

    let result = {};
    Object.keys(globalComponents).forEach(k => {
        let value = globalComponents[k];
        value = value.replace(/^okam\//, BUILTIN_COMPONENTS_PACKAGE_ROOT + appType + '/');
        let isRelMod = value.charAt(0) === '.';
        if (isRelMod) {
            value = pathUtil.join(sourceDir, value);
        }
        result[toHyphen(k)] = {
            isNpmMod: !isRelMod,
            modPath: value
        };
    });
    return result;
}

function initGlobalComponents ( appType , componentConf , sourceDir ) { let { global : globalComponents } = componentConf ; if ( ! globalComponents ) { return ; } let result = { } ; Object . keys ( globalComponents ) . forEach ( k => { let value = globalComponents [ k ] ; value = value . replace ( / ^okam\/ / , BUILTIN_COMPONENTS_PACKAGE_ROOT + appType + '/' ) ; let isRelMod = value . charAt ( 0 ) === '.' ; if ( isRelMod ) { value = pathUtil . join ( sourceDir , value ) ; } result [ toHyphen ( k ) ] = { isNpmMod : ! isRelMod , modPath : value } ; } ) ; return result ; }

Initialize the imported global component definition

@param {string} appType the app type to build
@param {Object} componentConf the component config
@param {string} sourceDir the source root directory
@return {Object}


function compile(file, options) {
    let obj = json5.parse(file.content.toString());
    let result = JSON.stringify(obj, null, 4);

    return {
        content: result
    };
}

function compile ( file , options ) { let obj = json5 . parse ( file . content . toString ( ) ) ; let result = JSON . stringify ( obj , null , 4 ) ; return { content : result } ; }

Compile the file using json5

@param {Object} file the file to process
@param {Object} options the compile options
@return {{content: string}}


function initPlatformInfo(callback) {
    api.getSystemInfo({
        success(info) {
            callback && callback(null, info);
        },
        fail(err) {
            callback && callback(err);
        }
    });
}

function initPlatformInfo ( callback ) { api . getSystemInfo ( { success ( info ) { callback && callback ( null , info ) ; } , fail ( err ) { callback && callback ( err ) ; } } ) ; }

Init platform info

@inner
@param {Function} callback the callback to execute when platform info get done