Compare commits
	
		
			7 Commits 
		
	
	
	| Author | SHA1 | Date | 
|---|---|---|
|  | 2164cb0137 | |
|  | 96b9554574 | |
|  | dd8ff91294 | |
|  | fa84cc0c5d | |
|  | 2b9599781e | |
|  | 7167a8b467 | |
|  | c3dd7a843c | 
|  | @ -23,9 +23,9 @@ | |||
| ### 你需要知道的几个事情 | ||||
| 
 | ||||
| - 因为没有打包, 所以所有的文件引用都是按源代码的结构, 对于源码的保护比较弱(虽然打包也没约等于没保护, 因为前端没秘密)。 | ||||
| - 因为是用的是原生的`ESM`,所以引用的**依赖/文件**, 需要完整的路径, 可以省略后缀名, 但不能省略`index.js/index.vue`。 | ||||
| - 因为是用的是原生的`ESM`,所以引用的**依赖/文件**, 需要完整的路径, 可以省略后缀名(不推荐), 但不能省略`index.js/index.vue`。 | ||||
| - 因为没有内置完整的样式处理,支持`scoped`、`:deep()`, 但不支持`:global()`。 | ||||
| - `单文件组件`中的样式, 如果是用scss, 不支持引用其他文件, 也不支持设置共用定义文件。 | ||||
| - `单文件组件`中的样式, 如果是用scss, 不支持引用其他文件。 | ||||
| - 样式预处理器, 只支持scss, 不支持less。 | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										7
									
								
								index.js
								
								
								
								
							
							
						
						
									
										7
									
								
								index.js
								
								
								
								
							|  | @ -19,6 +19,7 @@ const IS_WINDOWS = process.platform === 'win32' | |||
| const CONFIG_FILE = normalize(join(WORK_SPACE, 'fite.config.js')) | ||||
| const PROTOCOL = IS_WINDOWS ? 'file://' : '' | ||||
| const NODE_VERSION = process.versions.node.split('.').map(n => +n) | ||||
| const ABS_CONFIG_FILEPATH = PROTOCOL + CONFIG_FILE | ||||
| 
 | ||||
| let args = process.argv.slice(2) | ||||
| let mode = args.shift() || 'prod' | ||||
|  | @ -40,8 +41,9 @@ switch (mode) { | |||
|   case 'dev': | ||||
|     process.env.NODE_ENV = 'development' | ||||
| 
 | ||||
|     import(PROTOCOL + CONFIG_FILE) | ||||
|     import(ABS_CONFIG_FILEPATH) | ||||
|       .then(function (conf) { | ||||
|         conf.default.ABS_CONFIG_FILEPATH = ABS_CONFIG_FILEPATH | ||||
|         createServer(WORK_SPACE, conf.default) | ||||
|       }) | ||||
|       .catch(err => { | ||||
|  | @ -52,13 +54,14 @@ switch (mode) { | |||
|   case 'build': | ||||
|     process.env.NODE_ENV = 'production' | ||||
| 
 | ||||
|     import(PROTOCOL + CONFIG_FILE) | ||||
|     import(ABS_CONFIG_FILEPATH) | ||||
|       .then(function (conf) { | ||||
|         let dist = conf.buildDir || 'dist' | ||||
|         if (clean && fs.isdir(dist)) { | ||||
|           console.log('清除dist目录...') | ||||
|           fs.rm(dist) | ||||
|         } | ||||
|         conf.default.ABS_CONFIG_FILEPATH = ABS_CONFIG_FILEPATH | ||||
|         compile(WORK_SPACE, dist, conf.default, verbose) | ||||
|       }) | ||||
|       .catch(err => { | ||||
|  |  | |||
|  | @ -64,7 +64,7 @@ function scopeCss(css = '', hash) { | |||
|               if (last.startsWith(':')) { | ||||
|                 output = parseVDeep(last, output, true) | ||||
|               } else { | ||||
|                 output = `${last} ${output}` | ||||
|                 output = `${last}[data-${hash}] ${output}` | ||||
|               } | ||||
|             } else { | ||||
|               if (last.includes(':')) { | ||||
|  | @ -115,7 +115,7 @@ export function compileScss(file, inject = '') { | |||
| export function parseJs( | ||||
|   code = '', | ||||
|   imports, | ||||
|   { IS_MPA, currentPage, IS_ENTRY, DEPLOY_PATH, LEGACY_MODE } = {}, | ||||
|   { IS_MPA, currentPage, IS_ENTRY, DEPLOY_PATH, LEGACY_MODE, define } = {}, | ||||
|   filename, | ||||
|   linePatch = 1 | ||||
| ) { | ||||
|  | @ -241,6 +241,8 @@ export function parseJs( | |||
|             `  stylesheet.textContent = ${tmp};\n` + | ||||
|             `  document.head.appendChild(stylesheet);\n` + | ||||
|             `}\n` | ||||
| 
 | ||||
|           return `let ${tmp};\n!(async function(){\n  ${tmp} = await __fite_import('${name}', import.meta.url);\n})()` | ||||
|         } else { | ||||
|           // CSSStyleSheet.replaceSync 需要FF v101, Safari 16.4才支持
 | ||||
|           fixedStyle += | ||||
|  | @ -251,8 +253,9 @@ export function parseJs( | |||
|             `  __sheets__.push(stylesheet);\n` + | ||||
|             `  document.adoptedStyleSheets = __sheets__;\n` + | ||||
|             `}\n` | ||||
| 
 | ||||
|           return `const ${tmp} = await __fite_import('${name}', import.meta.url)` | ||||
|         } | ||||
|         return `const ${tmp} = await __fite_import('${name}', import.meta.url)` | ||||
|       } else { | ||||
|         if (name.startsWith('@/')) { | ||||
|           name = name.replace('@/', urlJoin(DEPLOY_PATH, ASSETS_DIR)) | ||||
|  | @ -287,6 +290,10 @@ export function parseJs( | |||
|       } | ||||
|     }) | ||||
| 
 | ||||
|   for (let key in define) { | ||||
|     code = code.replaceAll(key, define[key]) | ||||
|   } | ||||
| 
 | ||||
|   if (fixedStyle) { | ||||
|     code += '\n\n' + (IS_ENTRY ? SHEETS_DEF : '') + fixedStyle | ||||
| 
 | ||||
|  | @ -303,7 +310,7 @@ export function parseJs( | |||
|  * @param file <String> 文件路径 | ||||
|  * @return <String> 返回转换后的js代码 | ||||
|  */ | ||||
| export function compileVue(file, imports, options = {}) { | ||||
| export async function compileVue(file, imports, options = {}) { | ||||
|   // 修正那反人类的windows路径
 | ||||
|   let filename = file.slice(options.SOURCE_DIR.length).replace(/\\/g, '/') | ||||
|   let code = (fs.cat(file) || '').toString().replace(/\r\n/g, '\n') | ||||
|  | @ -333,10 +340,15 @@ export function compileVue(file, imports, options = {}) { | |||
|       } else { | ||||
|         css = compileScss(it[1], options.INJECT_SCSS) | ||||
|       } | ||||
| 
 | ||||
|       return css | ||||
|     }) | ||||
|     .join(' ') | ||||
| 
 | ||||
|   for (let fn of options.plugin) { | ||||
|     scss = await fn('css', scss) | ||||
|   } | ||||
| 
 | ||||
|   js = js ? js[1] : 'export default {}' | ||||
| 
 | ||||
|   try { | ||||
|  |  | |||
|  | @ -44,11 +44,14 @@ export async function compileFiles( | |||
|     switch (it.ext) { | ||||
|       case '.vue': | ||||
|         { | ||||
|           let code = compileVue(path, imports, options) | ||||
|           let code = await compileVue(path, imports, options) | ||||
| 
 | ||||
|           await Es.transform(code, { minify: true }).then(r => { | ||||
|           await Es.transform(code, { minify: true }).then(async ({ code }) => { | ||||
|             for (let fn of options.plugin) { | ||||
|               code = await fn('js', code) | ||||
|             } | ||||
|             fs.echo( | ||||
|               r.code, | ||||
|               code, | ||||
|               join(dist, 'assets/', pageDir, it.name.replace(/\.vue$/, '.js')) | ||||
|             ) | ||||
|           }) | ||||
|  | @ -61,8 +64,11 @@ export async function compileFiles( | |||
| 
 | ||||
|           code = parseJs(code + '', imports, options) | ||||
| 
 | ||||
|           await Es.transform(code, { minify: true }).then(r => { | ||||
|             fs.echo(r.code, join(dist, 'assets/', pageDir, it.name)) | ||||
|           await Es.transform(code, { minify: true }).then(async ({ code }) => { | ||||
|             for (let fn of options.plugin) { | ||||
|               code = await fn('js', code) | ||||
|             } | ||||
|             fs.echo(code, join(dist, 'assets/', pageDir, it.name)) | ||||
|           }) | ||||
|         } | ||||
|         break | ||||
|  | @ -91,6 +97,9 @@ export async function compileFiles( | |||
|             fs.cp(path, target) | ||||
|           } else { | ||||
|             let code = compileScss(path) | ||||
|             for (let fn of options.plugin) { | ||||
|               code = await fn('css', code) | ||||
|             } | ||||
|             fs.echo(code, target) | ||||
|           } | ||||
|         } | ||||
|  |  | |||
							
								
								
									
										48
									
								
								lib/dev.js
								
								
								
								
							
							
						
						
									
										48
									
								
								lib/dev.js
								
								
								
								
							|  | @ -34,6 +34,7 @@ export default async function createServer(root = '', conf = {}) { | |||
|   const LEGACY_MODE = !!conf.legacy | ||||
|   const ENABLE_GZIP = !!conf.devServer.gzip | ||||
|   const { isCustomElement = defaultCustomElement } = conf.compileOptions || {} | ||||
|   const { plugin = [], define = {} } = conf | ||||
| 
 | ||||
|   if (conf.imports['vue-dev']) { | ||||
|     conf.imports.vue = conf.imports['vue-dev'] | ||||
|  | @ -73,7 +74,7 @@ export default async function createServer(root = '', conf = {}) { | |||
|     currentPage = '' | ||||
| 
 | ||||
|   server | ||||
|     .on('request', function (req, res) { | ||||
|     .on('request', async function (req, res) { | ||||
|       let prefix = DEPLOY_PATH ? DEPLOY_PATH.replace(/\/$/, '') : '' | ||||
|       let url = | ||||
|         prefix && req.url.startsWith(prefix) | ||||
|  | @ -178,11 +179,18 @@ export default async function createServer(root = '', conf = {}) { | |||
|                   currentPage, | ||||
|                   IS_ENTRY: true, | ||||
|                   DEPLOY_PATH, | ||||
|                   LEGACY_MODE | ||||
|                   LEGACY_MODE, | ||||
|                   isCustomElement, | ||||
|                   plugin, | ||||
|                   define | ||||
|                 }, | ||||
|                 page.entry | ||||
|               ) | ||||
| 
 | ||||
|               for (let fn of plugin) { | ||||
|                 entry = await fn('js', entry) | ||||
|               } | ||||
| 
 | ||||
|               code = parseHtml(html, { | ||||
|                 page, | ||||
|                 imports: conf.imports, | ||||
|  | @ -215,7 +223,7 @@ export default async function createServer(root = '', conf = {}) { | |||
|                 return | ||||
|               } | ||||
| 
 | ||||
|               code = compileVue(file, conf.imports, { | ||||
|               code = await compileVue(file, conf.imports, { | ||||
|                 IS_MPA, | ||||
|                 currentPage, | ||||
|                 SOURCE_DIR, | ||||
|  | @ -223,9 +231,15 @@ export default async function createServer(root = '', conf = {}) { | |||
|                 DEPLOY_PATH, | ||||
|                 INJECT_SCSS, | ||||
|                 LEGACY_MODE, | ||||
|                 isCustomElement | ||||
|                 isCustomElement, | ||||
|                 plugin, | ||||
|                 define | ||||
|               }) | ||||
| 
 | ||||
|               for (let fn of plugin) { | ||||
|                 code = await fn('js', code) | ||||
|               } | ||||
| 
 | ||||
|               res.setHeader('content-type', MIME_TYPES.js) | ||||
|             } | ||||
|             break | ||||
|  | @ -251,15 +265,20 @@ export default async function createServer(root = '', conf = {}) { | |||
|                 return | ||||
|               } | ||||
|               code = compileScss(file) | ||||
|               for (let fn of plugin) { | ||||
|                 code = await fn('css', code) | ||||
|               } | ||||
|               res.setHeader('content-type', MIME_TYPES.css) | ||||
|             } | ||||
|             break | ||||
| 
 | ||||
|           case 'js': | ||||
|           case 'wasm': | ||||
|             { | ||||
|               let rpath = pathname.replace('@/', '') | ||||
|               let file | ||||
|               let isJson = false | ||||
|               let isWasm = rpath.endsWith('.wasm') | ||||
| 
 | ||||
|               if (rpath.endsWith('json.js')) { | ||||
|                 isJson = true | ||||
|  | @ -295,6 +314,8 @@ export default async function createServer(root = '', conf = {}) { | |||
|                 } catch (err) { | ||||
|                   console.log('%s 语法错误: %s', rpath, red(err.message)) | ||||
|                 } | ||||
|               } else if (isWasm) { | ||||
|                 //
 | ||||
|               } else { | ||||
|                 code = parseJs( | ||||
|                   code + '', | ||||
|  | @ -303,10 +324,16 @@ export default async function createServer(root = '', conf = {}) { | |||
|                     IS_MPA, | ||||
|                     currentPage, | ||||
|                     DEPLOY_PATH, | ||||
|                     LEGACY_MODE | ||||
|                     LEGACY_MODE, | ||||
|                     isCustomElement, | ||||
|                     plugin, | ||||
|                     define | ||||
|                   }, | ||||
|                   file | ||||
|                 ) | ||||
|                 for (let fn of plugin) { | ||||
|                   code = await fn('js', code) | ||||
|                 } | ||||
|               } | ||||
|               res.setHeader('content-type', MIME_TYPES[ext]) | ||||
|             } | ||||
|  | @ -387,7 +414,7 @@ export default async function createServer(root = '', conf = {}) { | |||
|       ) | ||||
|       chokidar | ||||
|         .watch([SOURCE_DIR, PUBLIC_DIR, join(root, './index.html')]) | ||||
|         .on('all', (act, filePath) => { | ||||
|         .on('all', async (act, filePath) => { | ||||
|           if (ready) { | ||||
|             let file = filePath.slice(SOURCE_DIR.length) | ||||
| 
 | ||||
|  | @ -407,6 +434,9 @@ export default async function createServer(root = '', conf = {}) { | |||
|                     } else { | ||||
|                       content = fs.cat(filePath).toString() | ||||
|                     } | ||||
|                     for (let fn of plugin) { | ||||
|                       content = await fn('css', content) | ||||
|                     } | ||||
|                     ws.send({ | ||||
|                       action: 'render', | ||||
|                       data: { path: file.replace(/\\/g, '/'), content } | ||||
|  | @ -416,7 +446,7 @@ export default async function createServer(root = '', conf = {}) { | |||
| 
 | ||||
|                 case 'vue': | ||||
|                   { | ||||
|                     let content = compileVue(filePath, conf.imports, { | ||||
|                     let content = await compileVue(filePath, conf.imports, { | ||||
|                       IS_MPA, | ||||
|                       currentPage, | ||||
|                       SOURCE_DIR, | ||||
|  | @ -424,7 +454,9 @@ export default async function createServer(root = '', conf = {}) { | |||
|                       DEPLOY_PATH, | ||||
|                       INJECT_SCSS, | ||||
|                       LEGACY_MODE, | ||||
|                       isCustomElement | ||||
|                       isCustomElement, | ||||
|                       plugin, | ||||
|                       define | ||||
|                     }) | ||||
|                     let tmp = CACHE[filePath] | ||||
|                     if (tmp.changed) { | ||||
|  |  | |||
							
								
								
									
										27
									
								
								lib/prod.js
								
								
								
								
							
							
						
						
									
										27
									
								
								lib/prod.js
								
								
								
								
							|  | @ -52,7 +52,13 @@ export default function compile(root = '', dist = '', conf = {}, verbose) { | |||
|   ) | ||||
|   const INJECT_SCSS = readFile(conf.inject?.scss) | ||||
|   const LEGACY_MODE = !!conf.legacy | ||||
|   const { isCustomElement } = conf.compileOptions || {} | ||||
|   const { | ||||
|     ABS_CONFIG_FILEPATH, | ||||
|     compileOptions = {}, | ||||
|     define = {}, | ||||
|     plugin = [] | ||||
|   } = conf | ||||
|   const { isCustomElement = defaultCustomElement } = compileOptions | ||||
| 
 | ||||
|   conf.inject = conf.inject || { scss: '' } | ||||
| 
 | ||||
|  | @ -65,13 +71,8 @@ export default function compile(root = '', dist = '', conf = {}, verbose) { | |||
|     DEPLOY_PATH, | ||||
|     INJECT_SCSS, | ||||
|     LEGACY_MODE, | ||||
|     // 线程通讯无法传递函数类型, 需要转为字符串, 之后再转回来
 | ||||
|     isCustomElement: | ||||
|       THREADS_NUM > 0 | ||||
|         ? isCustomElement | ||||
|           ? isCustomElement.toString() | ||||
|           : null | ||||
|         : isCustomElement || defaultCustomElement | ||||
|     ABS_CONFIG_FILEPATH, | ||||
|     define | ||||
|   } | ||||
| 
 | ||||
|   fs.ls(SOURCE_DIR, true).forEach(path => { | ||||
|  | @ -101,7 +102,10 @@ export default function compile(root = '', dist = '', conf = {}, verbose) { | |||
| 
 | ||||
|   // 创建线程池
 | ||||
|   if (THREADS_NUM > 0 && (IS_MPA || list.size > THREADS_NUM * 10)) { | ||||
|     for (let i = 0; i < THREADS_NUM; i++) { | ||||
|     // 页面数过少时, 线程数量不能比页面数多
 | ||||
|     let max = Math.min(THREADS_NUM, PAGES_KEYS.length) | ||||
| 
 | ||||
|     for (let i = 0; i < max; i++) { | ||||
|       WORKER_POOL.add( | ||||
|         new Worker(join(__dirname, './thread.js'), { | ||||
|           workerData: { | ||||
|  | @ -113,6 +117,8 @@ export default function compile(root = '', dist = '', conf = {}, verbose) { | |||
|         }) | ||||
|       ) | ||||
|     } | ||||
|   } else { | ||||
|     options.isCustomElement = isCustomElement | ||||
|   } | ||||
| 
 | ||||
|   // 优先处理静态目录, 之后的源码目录中, 以便如果有产生相同的文件名, 则覆盖静态目录中的文件
 | ||||
|  | @ -202,7 +208,8 @@ export default function compile(root = '', dist = '', conf = {}, verbose) { | |||
|         doJob() | ||||
|       } | ||||
|     } else { | ||||
|       options.isCustomElement = isCustomElement || defaultCustomElement | ||||
|       options.plugin = plugin | ||||
|       options.isCustomElement = isCustomElement | ||||
|       compileFiles(currentPage, page, list, options, { | ||||
|         verbose, | ||||
|         dist, | ||||
|  |  | |||
|  | @ -8,11 +8,17 @@ import { compileFiles } from './compile.js' | |||
| import { defaultCustomElement } from './utils.js' | ||||
| 
 | ||||
| const { options, verbose, dist, imports } = workerData | ||||
| const { ABS_CONFIG_FILEPATH } = options | ||||
| 
 | ||||
| options.isCustomElement = options.isCustomElement | ||||
|   ? Function('return ' + options.isCustomElement)() | ||||
|   : defaultCustomElement | ||||
| const { compileOptions = {}, plugin = [] } = await import( | ||||
|   ABS_CONFIG_FILEPATH | ||||
| ).then(r => r.default) | ||||
| 
 | ||||
| const { isCustomElement = defaultCustomElement } = compileOptions | ||||
| options.isCustomElement = isCustomElement | ||||
| options.plugin = plugin | ||||
| 
 | ||||
| //
 | ||||
| async function doJob(job) { | ||||
|   let [currentPage, { page, files }] = job.entries().next().value | ||||
| 
 | ||||
|  | @ -28,7 +34,7 @@ async function doJob(job) { | |||
|     dist, | ||||
|     imports | ||||
|   }) | ||||
|   parentPort.postMessage('ok') | ||||
|   parentPort.postMessage(true) | ||||
| } | ||||
| 
 | ||||
| parentPort.on('message', doJob) | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| { | ||||
|   "name": "fite", | ||||
|   "type": "module", | ||||
|   "version": "1.3.1", | ||||
|   "version": "1.4.4", | ||||
|   "bin": { | ||||
|     "fite": "index.js" | ||||
|   }, | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue