Skip to content

Commit

Permalink
feat: add some language utils
Browse files Browse the repository at this point in the history
  • Loading branch information
vagusX committed Sep 21, 2020
1 parent 57e2310 commit 7ce8c76
Show file tree
Hide file tree
Showing 10 changed files with 190 additions and 34 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
* `"configuration": "./language-configuration.json"` --> `"resovledConfiguration": require('./language-configuration.json')`
* `"path": "./syntaxes/JavaScript.tmLanguage.json"` --> `"resovledConfiguration": require('./syntaxes/JavaScript.tmLanguage.json')

### 加载策略
* 在知道需要加载文件后缀之后可以按照文件后缀去选择需要加载的插件包即可
* 可通过 `es/utils#hasLanguageId` 方法去判断是否支持对应的语言 id
* 可通过 `es/utils#getLanguageIdByExtnameAndFilename` 方法传递拓展名和文件名去获取对应的语言 desc,从 `extensionPackageName` 字段可看出对应的语言包是哪一个

### tmLanguage 文件
由于 require 语法导致在 webpack 中需要额外添加 raw-loader 去处理 tmLanguage 文件,且会导致 resovledConfiguration 的内容格式不统一,因此目前都通过 fork vscode 插件 [TextMate Languages] 将 tmLanguage 转成 json

Expand Down
23 changes: 23 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,26 @@ declare module '@ali/kaitian-textmate-languages' {
registerGrammar: (grammarContribution: any, extPath: any) => Promise<void>,
) => Promise<void>[];
}

interface LanguageDesc {
id: string;
extensions: string[];
aliases: string[];
configuration: string;
filenames?: string[];
firstLine?: string;
/**
* 拓展字段,用来标识当前 language 是来源于哪个插件语言包
*/
extensionPackageName?: string;
}

declare module '@ali/kaitian-textmate-languages/es/utils' {
export function getLanguageIdByExtnameAndFilename(extname: string, filename?: string): LanguageDesc | undefined;
export function hasLanguageId(languageId: string): boolean;
}

declare module '@ali/kaitian-textmate-languages/lib/utils' {
export function getLanguageIdByExtnameAndFilename(extname: string, filename?: string): LanguageDesc | undefined;
export function hasLanguageId(languageId: string): boolean;
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
"name": "@ali/kaitian-textmate-languages",
"version": "2.0.1",
"description": "",
"main": "index.js",
"main": "lib/loader.js",
"module": "es/loader.js",
"scripts": {
"download": "node scripts/update-extensions.js",
"generate": "rm -rf lib && node scripts/generate.js",
Expand Down
145 changes: 112 additions & 33 deletions scripts/generate.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ const prettier = require('prettier')
const bluebird = require('bluebird')
const stripJsonComments = require('strip-json-comments')

const entryTemplate = require('./entry-template/entry-template')
const entryTemplateEs = require('./entry-template/entry-template-es')
const entryTemplate = require('./template/entry')
const entryTemplateEs = require('./template/entry/index-es')

const extensionsDir = path.resolve(__dirname, '../extensions')
const targetDir = path.resolve(__dirname, '../lib')
Expand All @@ -19,6 +19,11 @@ templateSettings.escape = false

const ResolvedConfigFiled = 'resolvedConfiguration';

// format by prettier
function pretty(content) {
return prettier.format(content, require('../.prettierrc'));
}

class Extension {
constructor(extPath) {
this.extPath = extPath
Expand All @@ -32,17 +37,19 @@ class Extension {
const extPkgJson = await fse.readJSON(
path.resolve(this.extPath, './package.json')
)
await this.contributes(extPkgJson, targetDir, false)
await this.contributes(extPkgJson, targetEsDir, true)
// 将 package.json 内容挂到 Extension 实例上
this.pkgJson = extPkgJson
await this.contributes(targetDir, false)
await this.contributes(targetEsDir, true)
}

async contributes(extDesc, outDir, esMode = false) {
async contributes(outDir, esMode = false) {
// create a folder for ext
const extOutDir = path.join(outDir, extDesc.name)
const extOutDir = path.join(outDir, this.pkgJson.name)
await fse.ensureDir(extOutDir)
// handle languages/grammars
await this.collectLanguages(extDesc.contributes, extOutDir)
await this.collectGrammars(extDesc.contributes, extOutDir)
await this.collectLanguages(this.pkgJson.contributes, extOutDir)
await this.collectGrammars(this.pkgJson.contributes, extOutDir)
await this.writeEntry(extOutDir, esMode)
}

Expand Down Expand Up @@ -81,8 +88,7 @@ class Extension {

await fse.writeFile(
path.resolve(extOutDir, 'index.js'),
// format by prettier
prettier.format(content, require('../.prettierrc')),
pretty(content),
'utf8'
)
}
Expand All @@ -98,26 +104,35 @@ class Extension {
await bluebird.map(
languages,
language => {
if (language.configuration) {
const targetFilename = path.basename(language.configuration)
// 收集 language 并将 configuration#string 转成 require 语法
this.desc.languages.push({
...language,
configuration: './' + targetFilename.trim()
})

return this.copyFileWithoutComments(
path.resolve(this.extPath, language.configuration),
path.resolve(extOutDir, targetFilename)
)
if (!language.configuration) {
this.collectLanguage(language)
return
}
// 收集 language
this.desc.languages.push(language)

const targetFilename = path.basename(language.configuration)
// 收集 language 并将 configuration#string 转成 require 语法
this.collectLanguage({
...language,
configuration: './' + targetFilename.trim()
})

return this.copyFileWithoutComments(
path.resolve(this.extPath, language.configuration),
path.resolve(extOutDir, targetFilename)
)
},
{ concurrency: 3 }
)
}

collectLanguage(language) {
// 因为上方执行了两次 collect contributes 因此做了一个去重
if (!this.desc.languages.find(n => n.id === language.id)) {
// 收集 language
this.desc.languages.push(language)
}
}

// handle textmate grammars#path
async collectGrammars(contributes = {}, extOutDir) {
const { grammars } = contributes
Expand Down Expand Up @@ -169,30 +184,94 @@ class Extension {
}
}

async function copyDummyFiles(filename, content, esContent) {
// generate `grammar-list.js`
await fse.writeFile(
path.resolve(targetDir, filename),
pretty(content),
)
await fse.writeFile(
path.resolve(targetEsDir, filename),
pretty(esContent),
)
}

/**
* 将 language-list/grammar-list 生成
*/
async function generateListFiles(extMetaList) {
const languageList = []
const grammarList = []
// 将 language/grammar 收集
extMetaList
.forEach(extMeta => {
const { languages, grammars } = extMeta.toJSON()
const { name: extensionPackageName } = extMeta.pkgJson
for (const language of languages) {
languageList.push({
...language,
extensionPackageName,
})
}

for (const grammar of grammars) {
grammarList.push({
...grammar,
extensionPackageName,
})
}
})

// 排序
languageList.sort((a, b) => a.id - b.id)
grammarList.sort((a, b) => a.language - b.language)
// 将收集好的 language/grammar 生成一个文件
// generate `language-list.js`
await copyDummyFiles(
'./language-list.js',
`module.exports = ${JSON.stringify(languageList)}`,
`export default ${JSON.stringify(languageList)}`,
)

// generate `grammar-list.js`
await copyDummyFiles(
'./grammar-list.js',
`module.exports = ${JSON.stringify(grammarList)}`,
`export default ${JSON.stringify(grammarList)}`,
)

// copy `util.js`
await copyDummyFiles(
'./utils.js',
await fse.readFile(path.resolve(__dirname, './template/utils/index.js'), 'utf-8'),
await fse.readFile(path.resolve(__dirname, './template/utils/index-es.js'), 'utf-8'),
)
}

async function generate() {
try {
for (const dir of [targetDir, targetEsDir]) {
await fse.emptyDir(dir)
}
// copy `loader.js`
await fse.copyFile(
path.resolve(__dirname, './loaders/loader.js'),
path.resolve(targetDir, './loader.js')
)

// copy `loader-es.js`
await fse.copyFile(
path.resolve(__dirname, './loaders/loader-es.js'),
path.resolve(targetEsDir, './loader.js')
await copyDummyFiles(
'loader.js',
await fse.readFile(path.resolve(__dirname, './template/loader/index.js'), 'utf-8'),
await fse.readFile(path.resolve(__dirname, './template/loader/index-es.js'), 'utf-8'),
)

const extensionNames = await promisify(fs.readdir)(extensionsDir)

const extensionList = []
for (const extName of extensionNames) {
// read extension package.json
const extPath = path.resolve(extensionsDir, extName)
const extension = new Extension(extPath)
await extension.run()
extensionList.push(extension)
}
// 生成 meta 文件
await generateListFiles(extensionList)
} catch (err) {
console.log(err)
process.exit(128)
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
24 changes: 24 additions & 0 deletions scripts/template/utils/index-es.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import languageList from './language-list';

/**
* 通过 extname 配合 filename 获取对应的语言
* @param {string} extname
* @param {string} filename
*/
export const getLanguageIdByExtnameAndFilename = (extname, filename) => {
// 先用 filename 去查找
let result = languageList.find(language => Array.isArray(language.filenames) && language.filenames.includes(filename));
if (!result) {
result = languageList.find(language => Array.isArray(language.extensions) && language.extensions.includes(extname));
}
return result;
};

/**
* 是否存在对应的 language id
* @param {string} languageId
* @returns boolean
*/
export const hasLanguageId = (languageId) => {
return languageList.findIndex(language => language.id === languageId) > -1;
}
24 changes: 24 additions & 0 deletions scripts/template/utils/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const languageList = require('./language-list');

/**
* 通过 extname 配合 filename 获取对应的语言
* @param {string} extname
* @param {string} filename
*/
exports.getLanguageIdByExtnameAndFilename = (extname, filename) => {
// 先用 filename 去查找
let result = languageList.find(language => Array.isArray(language.filenames) && language.filenames.includes(filename));
if (!result) {
result = languageList.find(language => Array.isArray(language.extensions) && language.extensions.includes(extname));
}
return result;
};

/**
* 是否存在对应的 language id
* @param {string} languageId
* @returns boolean
*/
exports.hasLanguageId = (languageId) => {
return languageList.findIndex(language => language.id === languageId) > -1;
}

0 comments on commit 7ce8c76

Please sign in to comment.