├── .gitignore ├── asset ├── example.gif └── icon_r.png ├── CHANGELOG.md ├── .vscodeignore ├── .vscode ├── extensions.json ├── tasks.json ├── settings.json └── launch.json ├── tslint.json ├── src ├── vars.ts ├── sfc.d.ts ├── index.ts ├── extension.ts ├── util.ts ├── parseTemplate.ts └── scssManage.ts ├── tsconfig.json ├── LICENSE ├── package.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules 3 | .vscode-test/ 4 | *.vsix 5 | -------------------------------------------------------------------------------- /asset/example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/accforgit/AutoScssStruct4Vue/HEAD/asset/example.gif -------------------------------------------------------------------------------- /asset/icon_r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/accforgit/AutoScssStruct4Vue/HEAD/asset/icon_r.png -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [0.1.0] 4 | 5 | - Initial release 6 | 7 | ## [0.1.1] 8 | 9 | ### Changed 10 | 11 | - fix known problems -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/test/** 4 | src/** 5 | .gitignore 6 | vsc-extension-quickstart.md 7 | **/tsconfig.json 8 | **/tslint.json 9 | **/*.map 10 | **/*.ts -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "ms-vscode.vscode-typescript-tslint-plugin" 6 | ] 7 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-string-throw": true, 4 | "no-unused-expression": false, 5 | "no-duplicate-variable": true, 6 | "curly": false, 7 | "class-name": true, 8 | "semicolon": [ 9 | false, 10 | "always" 11 | ], 12 | "triple-equals": false, 13 | "no-any": false 14 | }, 15 | "defaultSeverity": "warning" 16 | } 17 | -------------------------------------------------------------------------------- /src/vars.ts: -------------------------------------------------------------------------------- 1 | import { IConfig } from './sfc.d' 2 | 3 | interface IVARS { 4 | config: IConfig 5 | } 6 | 7 | // 全局变量 8 | const VARS: IVARS = { 9 | config: { 10 | // 暂存当前插件的配置项,由 ./util.ts/updateConfig 方法进行更新 11 | autoScssStruct4VueConf: { 12 | excuteMode: '', 13 | scssFilePath: '' 14 | }, 15 | indenConf: { 16 | tabSize: 2 17 | } 18 | } 19 | } 20 | 21 | export default VARS 22 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "problemMatcher": "$tsc-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never" 13 | }, 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | }, 9 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 10 | "typescript.tsc.autoDetect": "off" 11 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "out", 6 | "lib": [ 7 | "es6" 8 | ], 9 | "sourceMap": true, 10 | "rootDir": "src", 11 | "strict": true /* enable all strict type-checking options */ 12 | /* Additional Checks */ 13 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 14 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 15 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 16 | }, 17 | "exclude": [ 18 | "node_modules", 19 | ".vscode-test" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 KQ 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Run Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "runtimeExecutable": "${execPath}", 13 | "args": [ 14 | "--extensionDevelopmentPath=${workspaceFolder}" 15 | ], 16 | "outFiles": [ 17 | "${workspaceFolder}/out/**/*.js" 18 | ], 19 | "preLaunchTask": "npm: watch" 20 | }, 21 | { 22 | "name": "Extension Tests", 23 | "type": "extensionHost", 24 | "request": "launch", 25 | "runtimeExecutable": "${execPath}", 26 | "args": [ 27 | "--extensionDevelopmentPath=${workspaceFolder}", 28 | "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" 29 | ], 30 | "outFiles": [ 31 | "${workspaceFolder}/out/test/**/*.js" 32 | ], 33 | "preLaunchTask": "npm: watch" 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /src/sfc.d.ts: -------------------------------------------------------------------------------- 1 | export interface IScssAst { 2 | // css 规则内容 3 | rule: string 4 | selectorNames: string 5 | children: Array 6 | parent?: IScssAst 7 | // 同级别元素,选择器相同,但子元素不同,需要按照出现的顺序进行标记是否已经被匹配比较过了 8 | hasMatch?: boolean, 9 | // 是否是以 @ 开头的关键字规则,例如 10 | isKeyRule?: boolean, 11 | // scss 注释 12 | comment?: string, 13 | // 换行、缩进字符信息 14 | rnInfo: { 15 | // 选择器字符前面的换行、缩进信息 16 | start?: string 17 | // 选择器字符 与 { 字符之间的换行、缩进信息 18 | startAfter?: string 19 | // } 字符前面的换行、缩进信息 20 | end?: string 21 | }, 22 | // 相比于上一次是否是新增节点 23 | isNew?: boolean 24 | } 25 | 26 | export interface ITemplateObj { 27 | // 选择器集合 28 | selectorNames: Array 29 | children: Array 30 | } 31 | 32 | export interface IDoc { 33 | // 类型,tag/text 34 | type: string 35 | content?: string 36 | // 是否是自闭合标签 37 | voidElement: boolean 38 | // 标签名 39 | name: string 40 | // 属性 41 | attrs: object | any 42 | children: IDoc[] 43 | // 使用了 v-bind 的属性 44 | bindAttrs: object | any 45 | // [propName: string]: any 46 | } 47 | 48 | export interface IConfig { 49 | autoScssStruct4VueConf: { 50 | // 执行方式 51 | excuteMode: string 52 | // scss保存路径 53 | scssFilePath: string 54 | }, 55 | indenConf: { 56 | tabSize: number 57 | } 58 | } -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import parseTemplate from './parseTemplate' 2 | import { scssStr2Ast, scssAstObj2Str, resetScss } from './scssManage' 3 | import { IScssAst } from './sfc.d' 4 | 5 | // 在 scss文本外再包裹一层,避免遗漏变量等内容 6 | const scssWrapper = 'wrapper {' 7 | 8 | export default (vueStr: string, scssStr?: string): string => { 9 | let originscssStr = '' 10 | let originscssMt = null 11 | let originTemplateStr = '' 12 | const originTemplateMt = vueStr.match(/]*>([\s\S]*)<\/template>/) 13 | if (originTemplateMt) { 14 | originTemplateStr = originTemplateMt[1].trim() 15 | } 16 | if (typeof scssStr === 'string') { 17 | originscssStr = scssStr 18 | } else { 19 | originscssMt = vueStr.match(/]*>([\s\S]*)<\/style>/) 20 | if (originscssMt) { 21 | originscssStr = originscssMt[1].trim() 22 | } 23 | } 24 | // 解析 template文本得到 templateAst对象 25 | const templateObj = parseTemplate('' + originTemplateStr + '') 26 | // 解析 scss文本得到 scssAst对象 27 | const scssObj = scssStr2Ast(scssWrapper + originscssStr + '}') 28 | // 根据 templateAst 重置 scssAst(只增不删) 29 | resetScss(templateObj, (scssObj as IScssAst)) 30 | // 将更新后的 scssAst转换为 scss文本 31 | const newscssStr = scssAstObj2Str((scssObj as IScssAst).children[0]).trim().slice(scssWrapper.length, -1).trimLeft() 32 | // 将 scss文本写入本地文件 33 | let newVueFile = '' 34 | if (typeof scssStr === 'string') { 35 | newVueFile = newscssStr 36 | } else { 37 | if (originscssMt) { 38 | const scssMt = vueStr.match(/([\s\S]*)(]*>)([\s\S]*)(<\/style>\s*)/) 39 | if (scssMt) { 40 | newVueFile = scssMt[1] + scssMt[2] + '\n' + newscssStr.trimRight() + '\n' + scssMt[4] 41 | } 42 | } else { 43 | newVueFile = vueStr + `\n\n` 44 | } 45 | } 46 | return newVueFile 47 | } 48 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode' 2 | import { excuteWhenSave, updateScssFile, generateProcess, updateConfig } from './util' 3 | import VARS from './vars' 4 | 5 | export function activate(context: vscode.ExtensionContext) { 6 | console.log('扩展激活') 7 | // 更新配置项变量 8 | updateConfig() 9 | let listener: vscode.Disposable | null = null 10 | // 监控配置项 change 11 | vscode.workspace.onDidChangeConfiguration(() => { 12 | // 更新配置项变量 13 | updateConfig() 14 | if (VARS.config.autoScssStruct4VueConf.excuteMode === 'onCommand') { 15 | // 从 onSave&onCommand 变成 onCommand 16 | if (!listener) return 17 | listener.dispose() 18 | } else { 19 | // 从 onCommand 变成 onSave&onCommand 20 | listener = excuteWhenSave() 21 | } 22 | }) 23 | if (VARS.config.autoScssStruct4VueConf.excuteMode === 'onSave&onCommand') { 24 | // 当文件保存时执行 25 | listener = excuteWhenSave() 26 | } 27 | // 当使用 autoScssStruct 命令时执行 28 | context.subscriptions.push(vscode.commands.registerTextEditorCommand('extension.autoScssStruct', async textEditor => { 29 | console.log('autoScssStruct 执行命令启动') 30 | if (!textEditor) return console.log('no textEditor') 31 | const activeDocument = textEditor.document 32 | // 当前文件未保存,则尝试保存 33 | if (activeDocument.isDirty) { 34 | let saveRst = false 35 | try { 36 | saveRst = await activeDocument.save() 37 | } catch (e) { 38 | console.log('save error:', e) 39 | } 40 | if (!saveRst) { 41 | return vscode.window.showInformationMessage('请先保存当前文件') 42 | } 43 | } 44 | if (activeDocument.languageId !== 'vue') { 45 | return vscode.window.showInformationMessage('当前不是 vue 文件') 46 | } 47 | const fileStr = generateProcess(activeDocument) 48 | updateScssFile(activeDocument.uri.fsPath, fileStr, () => { 49 | textEditor.edit((editorBuilder: vscode.TextEditorEdit) => { 50 | editorBuilder.replace(new vscode.Range(new vscode.Position(0, 0), new vscode.Position(activeDocument.lineCount + 1, 0)), fileStr) 51 | setTimeout(() => { 52 | activeDocument.save().then(rst => { 53 | console.log('保存结果:', rst) 54 | }) 55 | }, 0) 56 | }) 57 | }) 58 | })) 59 | } 60 | 61 | // this method is called when your extension is deactivated 62 | export function deactivate() { 63 | console.log('扩展释放') 64 | } 65 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "autoscssstruct4vue", 3 | "displayName": "AutoScssStruct4Vue", 4 | "description": "auto scss struct from template for vue", 5 | "version": "0.1.5", 6 | "author": "kother@foxmail.com", 7 | "publisher": "KQ", 8 | "engines": { 9 | "vscode": "^1.36.0" 10 | }, 11 | "categories": [ 12 | "Other" 13 | ], 14 | "icon": "asset/icon_r.png", 15 | "keywords": [ 16 | "scss", 17 | "scss", 18 | "auto", 19 | "auto scss", 20 | "auto scss", 21 | "autoscssstruct4Vue" 22 | ], 23 | "repository": { 24 | "type": "git", 25 | "url": "https://github.com/accforgit/AutoScssStruct4Vue.git" 26 | }, 27 | "bugs": { 28 | "url": "https://github.com/accforgit/AutoScssStruct4Vue/issues" 29 | }, 30 | "activationEvents": [ 31 | "onLanguage:vue" 32 | ], 33 | "main": "./out/extension.js", 34 | "contributes": { 35 | "commands": [ 36 | { 37 | "command": "extension.autoScssStruct", 38 | "title": "autoScssStruct" 39 | } 40 | ], 41 | "menus": { 42 | "editor/context": [ 43 | { 44 | "when": "editorFocus", 45 | "command": "extension.autoScssStruct", 46 | "group": "navigation" 47 | } 48 | ] 49 | }, 50 | "configuration": { 51 | "title": "AutoScssStruct4Vue", 52 | "properties": { 53 | "autoScssStruct4Vue.excuteMode": { 54 | "type": "string", 55 | "description": "在什么情况下重新生成 scss文件(when to excute)", 56 | "default": "onCommand", 57 | "enum": [ 58 | "onCommand", 59 | "onSave&onCommand" 60 | ], 61 | "enumDescriptions": [ 62 | "当使用右键菜单命令 autoScssStruct 命令时执行(excute when command)", 63 | "当保存时,以及当使用右键菜单命令 autoScssStruct 命令时执行(excute when save&command)" 64 | ] 65 | }, 66 | "autoScssStruct4Vue.scssFilePath": { 67 | "type": "string", 68 | "description": "编译生成的 scss字符串写入的文件路径,不传则默认写入当前 vue 文件的 style 标签内(如果文件不存在则自动创建)", 69 | "default": "" 70 | } 71 | } 72 | } 73 | }, 74 | "scripts": { 75 | "vscode:prepublish": "npm run compile", 76 | "compile": "tsc -p ./", 77 | "watch": "tsc -watch -p ./", 78 | "pretest": "npm run compile" 79 | }, 80 | "devDependencies": { 81 | "@types/glob": "^7.1.1", 82 | "@types/mocha": "^5.2.6", 83 | "@types/node": "^10.12.21", 84 | "@types/vscode": "^1.36.0", 85 | "glob": "^7.1.4", 86 | "mocha": "^10.2.0", 87 | "typescript": "^3.3.1", 88 | "tslint": "^5.12.1", 89 | "vscode-test": "^1.0.0-next.0" 90 | }, 91 | "dependencies": { 92 | }, 93 | "license": "MIT" 94 | } 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AutoScssStruct4Vue 2 | 3 | 根据 `vue`文件的模板`template`结构,自动生成对应的 `scss`文件 4 | 5 | ![img](./asset/example.gif) 6 | 7 | ## Features 8 | 9 | - 识别 `vue`内置元素和组件标签 10 | - 过滤同级重复的选择器 11 | - 只在原先的基础上增加新的选择器规则,不删减 12 | 13 | ## Example 14 | 15 | ### example_1 16 | 17 | ```html 18 | 29 | ``` 30 | 扩展执行编译后: 31 | ```html 32 | 43 | 55 | ``` 56 | 57 | ### example_2 58 | ```html 59 | 72 | 87 | ``` 88 | 扩展执行编译后: 89 | ```html 90 | 103 | 123 | ``` 124 | 125 | ## 配置项(Settings) 126 | 127 | ### autoscssStruct4Vue.excuteMode 128 | 129 | 在什么情况下重新生成 `scss`文件,提供两种选择: 130 | 131 | - 当使用 `autoscssStructWhenCommand` 命令时执行(`excute when command`)(**default**) 132 | 133 | 即使用 `vscode`命令:`ctrl+shift+p`,选中 `autoscssStructWhenCommand`命令时,扩展执行 134 | 135 | 或者在编辑器内,右键,选中菜单中的 `autoscssStructWhenCommand`时,扩展执行 136 | 137 | - 当保存时,以及当使用 `autoscssStructWhenCommand` 命令时执行(`excute when save&command`) 138 | 139 | 在上一条的基础上,增加了当文件保存时,扩展执行 140 | 141 | ### autoscssStruct4Vue.scssFilePath 142 | 143 | 编译生成的 `scss`字符串写入的文件相对路径(相对于当前文件) 144 | 145 | 如果此配置项为空,则写入当前 `vue`文件的 `