Skip to content

vsc base compiling script

Alf Nielsen edited this page Apr 14, 2019 · 1 revision

Meta compiling

vsc-base and vsc-base.org is compiled using.... tadaah vsc-script using vsc-base

The vsc-base project is split into 3 area (All living in this mono-respo)

  • The npm module (vsc-base),
  • The vscode extension (vsc-script),
  • The the documantation website (vsc-base.org)

The actual development of vsc-base require that it can be run in a context with vscode (aka in an extension).

Because of this the actual development of the vsc-base (npm module) is done inside the vsc-script project.

So one question come up here? How can I easily update the documentation on vsc-base.org, and move (copy) source code to the real vsc-base project.

And the answers is of course.... tadaah vsc-script!

compilinVSC.vsc-script.ts

The the script is actual pretty simple.

It requires the source code to be written in one patter, so that it can use RegExp to split it up and map it.

The map is then used and create React components for the vsc-base.org project. For the vsc-base npm module its' just create a copy the files.

Note the version of the script describe here is an example written (14/04/2019) and it can have changed since in the source code!

import * as vsc from 'vsc-base'
/**
 * This script finds all const names in a file (From start of lines) and append the list to the end of that file.
 */
export async function run(path: string) {
   vsc.showMessage('Start compiling vsc...')
   // Find all files under vsc-base-development folder with starting name 'vsc-base-'
   const vscFiles = await vsc.findFilePaths(
      '**/vsc-base-development/vsc-base-*.ts'
   )
   // create a part of combined from all files
   const parts = await createPartMap(vscFiles)
   // get the dir (vsc-script/src/vsc-base-development), the script don't care where you click to start it!
   let [dir] = vsc.splitPath(vscFiles[0])
   dir = vsc.trimDashes(dir)
   // Now create all element/files used by the the vsc-base.org project
   await CompileToVscBaseOrg(dir, parts)

   // Now Copy the source files the vsc-base project
   for (const filePath of vscFiles) {
      const newPath = filePath.replace('vsc-script/src/vsc-base-development', 'vsc-base/src');
      await vsc.copy(filePath, newPath)
   }
   const basePath = dir + '/vsc-base.ts';
   const newPath = basePath.replace('vsc-script/src/vsc-base-development', 'vsc-base/src');
   await vsc.copy(basePath, newPath)

}
(...)

createPartMap

All source code for vsc-base is writting with JsDoc,

all part of the jsDoc most be annotation '@property'

and the code it self most always start with a export const {name} declaration

/**
 * @description 
 * Generate relative path between two paths.
 * @see http://vsc-base.org/#getRelativePath
 * @param fromPath
 * @param toPath
 * @vscType Raw
 * @oneLineEx const relativePath = vsc.getRelativePath(fromPath, toPath)
 * @testPrinterArgument
 { 
    fromPath: 'c:/somefolder/sub1/sub2/someFile.js',
    toPath: 'c:/somefolder/other/someFile.js'
 }
 * @testPrinter (args, printResult) => {
   const relativePath = vsc.getRelativePath(args.fromPath, args.toPath)
   printResult(relativePath)
}
 * @dependencyInternal sharedPath, splitPath, subtractPath
 * @returns string
 */
export const getRelativePath = (fromPath: string, toPath: string): string => {
   const _sharedPath = vsc.sharedPath(fromPath, toPath)
   const [fromDir] = vsc.splitPath(fromPath)
   const [toDir] = vsc.splitPath(toPath)
   const fromPathDownToShared = vsc.subtractPath(fromDir, _sharedPath)
   let toPathDownToShared = vsc.subtractPath(toDir, _sharedPath)
   const backPath = fromPathDownToShared
      .split(/\//)
      .map(_ => '../')
      .join('')
   const relativePath = backPath + toPathDownToShared
   return relativePath
}

The createParts method goes through all vsc-base files, split them up using '\n/**' Then split each part up and extract source-code and each jsDoc properties in to a partsMap.

type CodePart = {
   meta: string
   body: string
   name: string
   annotationName: string
   metaMapRaw: { [key: string]: string }
   metaMap: { [key: string]: string }
}
const createPartMap = async (vscFiles: string[]) => {
   const parts: CodePart[] = []
   for (const filePath of vscFiles) {
      const rawSource = await vsc.getFileContent(filePath)
      const rawParts = rawSource.split(/\n\/\*\*\s*\n/) // split usin \n/**
      rawParts.shift() // <-- This is leading imports (er empty area)
      rawParts.forEach(part => {
         const metaBody = part.split(/\n\s*\*\/\s*\n/)
         const meta = metaBody[0]
         const body = metaBody[1]
         //name
         const nameMatch = body.match(/^[\s\n]*(?:export\s+)const\s+(\w+)/)
         if (!nameMatch) {
            vsc.showErrorMessage('Did not find method name!!: ' + body)
            return
         }
         const name = nameMatch[1]
         //meta map:
         const metaMap: { [key: string]: string } = {}
         const metaMapRaw: { [key: string]: string } = {} // keep '*' instart of lines
         const mapArgs = meta.split(/\n?\s+\*\s+@/)
         mapArgs.shift() // remove leading empty area
         mapArgs.forEach(arg => {
            const argNameMatch = arg.match(/^\w+/)
            const argName = argNameMatch[0]
            const argContentRaw = arg.replace(/^\w+\s/, '')
            const argContent = argContentRaw.replace(/(^|\n)\s+\*/g, '\n')
            if (metaMap[argName]) {
               metaMap[argName] += ', ' + argContent
               metaMapRaw[argName] += ', ' + argContentRaw
            } else {
               metaMap[argName] = argContent
               metaMapRaw[argName] = argContentRaw
            }
         })
         let annotationName = vsc.toPascalCase(name + 'AnnotatedCode')
         if (!body.match(/^\s*$/)) {
            parts.push({ meta, body, name, metaMap, metaMapRaw, annotationName })
         }
      })
   }
   parts.sort((a, b) => a.body.localeCompare(b.body))
   return parts
}

After the Parts has been mapped all other method use the part to create the file it need.

A last note is that the writeAnnotationComponent removes some of the jsDocs, that is displayed in the Annotation (So people dont need to see that twice when looking at the documentation)

const CompileToVscBaseOrg = async (dir: string, parts: CodePart[]) => {
   // For vsc-base.org we change the path to point into that project (in this mono-respose)
   const orgDir = dir.replace('/vsc-script/src/vsc-base-development', '/vsc-base.org/src/allAnnotations');
   const anoDir = orgDir + '/annotations'
   // Create all code Annotation Components
   await writeAllAnotationComponent(anoDir, parts)
   // Create main Coponent for the annotations:
   await writeMainAnnotationComponent(orgDir, parts);
   // To enable live testing on vsc-base we copy the 'vsc-base-raw.ts' vsc-base.org aswell.
   const rawPath = dir + '/vsc-base-raw.ts';
   const newRawPath = rawPath.replace('/vsc-script/src/vsc-base-development', '/vsc-base.org/src/allAnnotations');
   let rawPathContent = await vsc.getFileContent(rawPath);
   // reaplce import vsc-base to point to itself instead!
   // In vsc-base.org we connect use vscode or system,
   // so we need to chenge the vsc-base-raw file just a little, so it dont point at the general vsc-base,
   // but instead point at itself!
   rawPathContent = rawPathContent.replace("import * as vsc from './vsc-base'", "import * as vsc from './vsc-base-raw'")
   // Save the modified vsc-base-raw to vsc-base-org project:
   await vsc.saveFileContent(newRawPath, rawPathContent)

}

const writeAllAnotationComponent = async (anoDir: string, parts: CodePart[]) => {
   // vsc-base.org has a React called Annotation (which is the one this script maps to)
   for (const part of parts) {
      // Create a Annotation React component
      const ano = writeAnnotationComponent(
         part.annotationName,
         part.name,
         part.body,
         part.metaMap,
         part.metaMapRaw
      )
      // and save it in the vsc-base.org folder for annotations (/vsc-base.org/src/allAnnotations/annotations)
      await vsc.saveFileContent(`${anoDir}/${part.annotationName}.tsx`, ano)
   }
}
const writeMainAnnotationComponent = async (dir: string, parts: CodePart[]) => {
   // Create main React component with all the annotations. (Called: AllAnnotations)
   // The parts.map creates a list of imports, and a list of written components.
   const allAnnotationsContent = `import React from 'react'

${parts.map(part => `import ${part.annotationName} from './annotations/${part.annotationName}'`).join('\n')}

const AllAnnotations = () =>
<>
${parts.map(part => `      <${part.annotationName} />`).join('\n')}
</>

export default AllAnnotations
`
   // Save the AllAnnotations component in the vsc-base.org project (/vsc-base.org/src/allAnnotations)
   await vsc.saveFileContent(`${dir}/AllAnnotations.tsx`, allAnnotationsContent)
}

const writeAnnotationComponent = (
   componentName: string,
   name: string,
   code: string,
   metaMap: { [key: string]: string },
   metaMapRaw: { [key: string]: string }
) => {
   // meta
   const writeMetaMap = []
   const excludeList = ['description', 'see', 'oneLineEx', 'ex']
   for (const [key, content] of Object.entries(metaMapRaw)) {
      if (!excludeList.includes(key)) {
         const escapedContent = content.replace(/([\\`\$\{])/g, '\\$1')
         let m = ` * @${key} ${escapedContent}`
         writeMetaMap.push(m)
      }
   }
   const meta = writeMetaMap.join('\n');
   //desription
   let descr = metaMap.description;
   const newLineREg = /\\\n/g
   descr = descr.replace(/(https?:\/\/[^\s]+)/g, `<a href='$1'>$1</a>`)
   descr = `<p>
                  ${descr.replace(newLineREg, '\n               </p>\n               <p>\n               ')}
               </p>`
   // online ex
   const oneLineEx = metaMap.oneLineEx.replace(/([\\`\$\{])/g, '\\$1');//.replace(/`/g, '\\`').replace(/\$\{/g, '\\${')
   // ex
   const codeEx = (metaMap.ex || '').replace(/([\\`\$\{])/g, '\\$1')
   code = code.replace(/([\\`\$\{])/g, '\\$1')

   let test = ''

   if (metaMap.testPrinterArgument && metaMap.testPrinter) {
      test = `
      test={
         <MethodTest
            initialArgs={${metaMap.testPrinterArgument}}
            onClickCall={${metaMap.testPrinter}}
         />
      }
      `
   }

   return `import React from 'react'
import AnnotatedCode from 'components/AnnotatedCode/AnnotatedCode'

${test === '' ? '' : `
import * as vsc from '../vsc-base-raw'

import MethodTest from 'components/MethodTest/MethodTest'
`}

const ${componentName} = () => {
   return (
      <AnnotatedCode
         id={'${name}'}
         title={'${name}'}
         annotation={
            <>
               ${descr}
            </>
         }
         ${test}
         codeOneLineEx={\`${oneLineEx}\`}
         codeEx={\`${codeEx}\`}
         code={\`/**
${meta}
 */
${code}\`}
      />
   )
}

export default ${componentName}

`
}
``
Clone this wiki locally