├── .chglog ├── CHANGELOG.tpl.md ├── RELEASE.tpl.md ├── config.yml └── release-config.yml ├── .editorconfig ├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ ├── dependabot-auto-merge.yml │ ├── github-ci.yml │ └── reuse-compliance.yml ├── .gitignore ├── .npmrc ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.txt ├── LICENSES └── Apache-2.0.txt ├── README.md ├── REUSE.toml ├── SECURITY.md ├── eslint.config.mjs ├── lib ├── Compiler.js ├── diff.js ├── fileUtils.js ├── index.js ├── less │ ├── fileLoader.js │ ├── importsPush.js │ └── parser.js ├── plugin │ ├── css-variables-collector.js │ ├── css-variables-pointer.js │ ├── import-collector.js │ ├── rtl.js │ ├── url-collector.js │ └── variable-collector.js ├── scope.js ├── themingParameters │ └── dataUri.js └── thirdparty │ └── less │ ├── README.md │ ├── colors.js │ ├── encoder.js │ ├── env.js │ ├── extend-visitor.js │ ├── functions.js │ ├── import-visitor.js │ ├── index.js │ ├── join-selector-visitor.js │ ├── lessc_helper.js │ ├── parser.js │ ├── source-map-output.js │ ├── to-css-visitor.js │ ├── tree.js │ ├── tree │ ├── alpha.js │ ├── anonymous.js │ ├── assignment.js │ ├── call.js │ ├── color.js │ ├── comment.js │ ├── condition.js │ ├── dimension.js │ ├── directive.js │ ├── element.js │ ├── expression.js │ ├── extend.js │ ├── import.js │ ├── keyword.js │ ├── media.js │ ├── mixin.js │ ├── negative.js │ ├── operation.js │ ├── paren.js │ ├── quoted.js │ ├── rule.js │ ├── ruleset.js │ ├── selector.js │ ├── unicode-descriptor.js │ ├── url.js │ ├── value.js │ └── variable.js │ └── visitor.js ├── package-lock.json ├── package.json └── test ├── common └── helper.js ├── expected ├── complex │ ├── test-cssvars-skeleton.css │ ├── test-cssvars-variables.css │ ├── test-cssvars-variables.source.less │ ├── test.css │ └── test.min.css ├── imports │ ├── main-no-relativeUrls.css │ └── main.css ├── libraries │ ├── empty │ │ ├── library-RTL.css │ │ ├── library-parameters.json │ │ └── library.css │ ├── lib1 │ │ └── my │ │ │ └── ui │ │ │ └── lib │ │ │ └── themes │ │ │ ├── base │ │ │ ├── library-RTL.css │ │ │ └── library.css │ │ │ └── foo │ │ │ ├── css_variables.css │ │ │ ├── css_variables.source.less │ │ │ ├── library-RTL.css │ │ │ ├── library.css │ │ │ ├── library_skeleton-RTL.css │ │ │ └── library_skeleton.css │ ├── lib2 │ │ └── my │ │ │ └── ui │ │ │ └── lib │ │ │ └── themes │ │ │ └── bar │ │ │ ├── library-RTL.css │ │ │ └── library.css │ ├── lib3 │ │ └── my │ │ │ └── other │ │ │ └── ui │ │ │ └── lib │ │ │ └── themes │ │ │ ├── bar │ │ │ ├── library-RTL.css │ │ │ └── library.css │ │ │ ├── base │ │ │ ├── library-RTL.css │ │ │ └── library.css │ │ │ └── foo │ │ │ ├── library-RTL.css │ │ │ └── library.css │ ├── lib4 │ │ └── my │ │ │ └── ui │ │ │ └── lib │ │ │ └── themes │ │ │ └── cascading │ │ │ ├── library-RTL.css │ │ │ └── library.css │ ├── lib5 │ │ └── my │ │ │ └── ui │ │ │ └── lib │ │ │ └── themes │ │ │ └── cascading │ │ │ ├── library-RTL.css │ │ │ └── library.css │ └── scopes │ │ ├── comments │ │ ├── lib1 │ │ │ └── comments │ │ │ │ └── themes │ │ │ │ └── foo │ │ │ │ ├── library-RTL.css │ │ │ │ └── library.css │ │ ├── lib2 │ │ │ └── comments │ │ │ │ └── themes │ │ │ │ └── bar │ │ │ │ ├── library-RTL.css │ │ │ │ └── library.css │ │ └── lib3 │ │ │ └── comments │ │ │ └── themes │ │ │ └── bar │ │ │ ├── library-RTL.css │ │ │ └── library.css │ │ ├── css-scope-root │ │ ├── lib1 │ │ │ └── css-scope-root │ │ │ │ └── themes │ │ │ │ └── foo │ │ │ │ ├── library-RTL.css │ │ │ │ └── library.css │ │ └── lib2 │ │ │ └── css-scope-root │ │ │ └── themes │ │ │ └── bar │ │ │ ├── library-RTL.css │ │ │ └── library.css │ │ ├── default │ │ ├── lib1 │ │ │ └── default │ │ │ │ └── themes │ │ │ │ └── foo │ │ │ │ ├── library-RTL.css │ │ │ │ └── library.css │ │ └── lib2 │ │ │ └── default │ │ │ └── themes │ │ │ └── bar │ │ │ ├── library-RTL.css │ │ │ └── library.css │ │ ├── dom │ │ ├── lib1 │ │ │ └── dom │ │ │ │ └── themes │ │ │ │ └── foo │ │ │ │ ├── library-RTL.css │ │ │ │ └── library.css │ │ └── lib2 │ │ │ └── dom │ │ │ └── themes │ │ │ └── bar │ │ │ ├── library-RTL.css │ │ │ └── library.css │ │ ├── empty-media-queries │ │ ├── lib1 │ │ │ └── empty-media-queries │ │ │ │ └── themes │ │ │ │ └── foo │ │ │ │ ├── library-RTL.css │ │ │ │ └── library.css │ │ └── lib2 │ │ │ └── empty-media-queries │ │ │ └── themes │ │ │ └── bar │ │ │ ├── library-RTL.css │ │ │ └── library.css │ │ ├── html-compressed │ │ ├── lib1 │ │ │ └── html │ │ │ │ └── themes │ │ │ │ └── foo │ │ │ │ ├── library-RTL.css │ │ │ │ └── library.css │ │ └── lib2 │ │ │ └── html │ │ │ └── themes │ │ │ └── bar │ │ │ ├── library-RTL.css │ │ │ └── library.css │ │ ├── html │ │ ├── lib1 │ │ │ └── html │ │ │ │ └── themes │ │ │ │ └── foo │ │ │ │ ├── library-RTL.css │ │ │ │ └── library.css │ │ └── lib2 │ │ │ └── html │ │ │ └── themes │ │ │ └── bar │ │ │ ├── library-RTL.css │ │ │ └── library.css │ │ ├── media-queries │ │ ├── lib1 │ │ │ └── media-queries │ │ │ │ └── themes │ │ │ │ └── foo │ │ │ │ ├── library-RTL.css │ │ │ │ └── library.css │ │ └── lib2 │ │ │ └── media-queries │ │ │ └── themes │ │ │ └── bar │ │ │ ├── library-RTL.css │ │ │ └── library.css │ │ ├── mixins │ │ ├── lib1 │ │ │ └── mixins │ │ │ │ └── themes │ │ │ │ └── foo │ │ │ │ ├── library-RTL.css │ │ │ │ └── library.css │ │ └── lib2 │ │ │ └── mixins │ │ │ └── themes │ │ │ └── bar │ │ │ ├── library-RTL.css │ │ │ └── library.css │ │ └── multiple-imports │ │ ├── lib1 │ │ └── multiple-imports │ │ │ └── themes │ │ │ └── foo │ │ │ ├── library-RTL.css │ │ │ └── library.css │ │ └── lib2 │ │ └── multiple-imports │ │ └── themes │ │ └── bar │ │ ├── library-RTL.css │ │ └── library.css ├── rtl │ ├── background-position.css │ ├── background.css │ ├── border.css │ ├── cursor.css │ ├── gradient.css │ ├── image-url.css │ ├── misc.css │ ├── shadow.css │ ├── transform.css │ └── variables.css ├── simple │ ├── test-RTL.css │ ├── test-RTL.min.css │ ├── test-cssvars-skeleton.css │ ├── test-cssvars-variables.css │ ├── test-cssvars-variables.source.less │ ├── test-inline-parameters.css │ ├── test-variables.json │ ├── test-variables.min.json │ ├── test.css │ └── test.min.css └── variables │ ├── main.css │ └── variables.json ├── fixtures ├── complex │ └── test.less ├── diff │ └── css │ │ ├── library1 │ │ ├── base.css │ │ └── compare.css │ │ └── library2 │ │ ├── base.css │ │ └── compare.css ├── error │ ├── main.less │ └── undefined-var.less ├── imports │ ├── dir1 │ │ └── foo.less │ ├── dir2 │ │ └── bar.less │ ├── dir3 │ │ ├── external.css │ │ └── inline.css │ └── main.less ├── libraries │ ├── empty │ │ └── empty.less │ ├── lib1 │ │ ├── my │ │ │ └── ui │ │ │ │ └── lib │ │ │ │ └── themes │ │ │ │ ├── base │ │ │ │ ├── global.less │ │ │ │ └── library.source.less │ │ │ │ └── foo │ │ │ │ ├── global.less │ │ │ │ └── library.source.less │ │ └── sap │ │ │ └── ui │ │ │ └── core │ │ │ └── themes │ │ │ └── foo │ │ │ └── .theming │ ├── lib2 │ │ ├── my │ │ │ └── ui │ │ │ │ └── lib │ │ │ │ └── themes │ │ │ │ └── bar │ │ │ │ ├── global.less │ │ │ │ └── library.source.less │ │ └── sap │ │ │ └── ui │ │ │ └── core │ │ │ └── themes │ │ │ └── bar │ │ │ └── .theming │ ├── lib3 │ │ └── my │ │ │ └── other │ │ │ └── ui │ │ │ └── lib │ │ │ └── themes │ │ │ ├── bar │ │ │ └── library.source.less │ │ │ ├── base │ │ │ ├── MyControl.less │ │ │ ├── library.source.less │ │ │ └── sub-directory │ │ │ │ └── MyOtherControl.less │ │ │ └── foo │ │ │ ├── MyControl.less │ │ │ └── library.source.less │ ├── lib4 │ │ └── my │ │ │ └── ui │ │ │ └── lib │ │ │ └── themes │ │ │ └── cascading │ │ │ ├── indirect-a.less │ │ │ ├── indirect-b.less │ │ │ ├── library.source.less │ │ │ ├── override.less │ │ │ └── variables.less │ ├── lib5 │ │ └── my │ │ │ └── ui │ │ │ └── lib │ │ │ └── themes │ │ │ └── cascading │ │ │ ├── base │ │ │ └── library.source.less │ │ │ ├── library.source.less │ │ │ └── plus │ │ │ └── library.source.less │ └── scopes │ │ ├── comments │ │ ├── lib1 │ │ │ └── comments │ │ │ │ └── themes │ │ │ │ └── foo │ │ │ │ └── library.source.less │ │ ├── lib2 │ │ │ └── comments │ │ │ │ └── themes │ │ │ │ └── bar │ │ │ │ └── library.source.less │ │ └── lib3 │ │ │ └── comments │ │ │ └── themes │ │ │ ├── bar │ │ │ ├── library.source.less │ │ │ └── my2.less │ │ │ ├── my3.less │ │ │ └── other │ │ │ └── sub │ │ │ └── my.less │ │ ├── css-scope-root │ │ ├── lib1 │ │ │ └── css-scope-root │ │ │ │ └── themes │ │ │ │ └── foo │ │ │ │ └── library.source.less │ │ └── lib2 │ │ │ └── css-scope-root │ │ │ └── themes │ │ │ └── bar │ │ │ └── library.source.less │ │ ├── default │ │ ├── lib1 │ │ │ └── default │ │ │ │ └── themes │ │ │ │ └── foo │ │ │ │ └── library.source.less │ │ └── lib2 │ │ │ └── default │ │ │ └── themes │ │ │ └── bar │ │ │ └── library.source.less │ │ ├── dom │ │ ├── lib1 │ │ │ └── dom │ │ │ │ └── themes │ │ │ │ └── foo │ │ │ │ └── library.source.less │ │ └── lib2 │ │ │ └── dom │ │ │ └── themes │ │ │ └── bar │ │ │ └── library.source.less │ │ ├── empty-media-queries │ │ ├── lib1 │ │ │ └── empty-media-queries │ │ │ │ └── themes │ │ │ │ └── foo │ │ │ │ └── library.source.less │ │ └── lib2 │ │ │ └── empty-media-queries │ │ │ └── themes │ │ │ └── bar │ │ │ └── library.source.less │ │ ├── html │ │ ├── lib1 │ │ │ └── html │ │ │ │ └── themes │ │ │ │ └── foo │ │ │ │ └── library.source.less │ │ └── lib2 │ │ │ └── html │ │ │ └── themes │ │ │ └── bar │ │ │ └── library.source.less │ │ ├── media-queries │ │ ├── lib1 │ │ │ └── media-queries │ │ │ │ └── themes │ │ │ │ └── foo │ │ │ │ └── library.source.less │ │ └── lib2 │ │ │ └── media-queries │ │ │ └── themes │ │ │ └── bar │ │ │ └── library.source.less │ │ ├── mixins │ │ ├── lib1 │ │ │ └── mixins │ │ │ │ └── themes │ │ │ │ └── foo │ │ │ │ └── library.source.less │ │ └── lib2 │ │ │ └── mixins │ │ │ └── themes │ │ │ └── bar │ │ │ └── library.source.less │ │ └── multiple-imports │ │ ├── lib1 │ │ └── multiple-imports │ │ │ └── themes │ │ │ └── foo │ │ │ └── library.source.less │ │ └── lib2 │ │ └── multiple-imports │ │ └── themes │ │ └── bar │ │ └── library.source.less ├── rootPaths │ ├── lib1 │ │ └── my │ │ │ └── themes │ │ │ └── foo │ │ │ └── foo.less │ └── lib2 │ │ └── my │ │ └── themes │ │ └── bar │ │ └── bar.less ├── rtl │ ├── background-position.less │ ├── background.less │ ├── border.less │ ├── cursor.less │ ├── gradient.less │ ├── image-url.less │ ├── img-RTL │ │ ├── column_header.gif │ │ ├── column_header2.gif │ │ ├── column_header3.gif │ │ ├── drop-down_ico.png │ │ ├── fg │ │ │ └── hi.png │ │ └── hover_column_header.gif │ ├── misc.less │ ├── shadow.less │ ├── transform.less │ └── variables.less ├── simple │ └── test.less └── variables │ └── main.less ├── lib ├── Compiler.js └── themingParameters │ └── dataUri.js ├── test-css-vars.js ├── test-custom-fs.js ├── test-diff.js ├── test-perf-workaround.js ├── test-url-collector-plugin.js ├── test-variable-collector.js └── test.js /.chglog/RELEASE.tpl.md: -------------------------------------------------------------------------------- 1 | {{ range .Versions }} 2 | {{ range .CommitGroups -}} 3 | ### {{ .Title }} 4 | {{ range .Commits -}} 5 | - {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} [`{{ .Hash.Short }}`]({{ $.Info.RepositoryURL }}/commit/{{ .Hash.Long }}) 6 | {{ end }} 7 | {{ end -}} 8 | 9 | {{- if .RevertCommits -}} 10 | ### Reverts 11 | {{ range .RevertCommits -}} 12 | - {{ .Revert.Header }} 13 | {{ end }} 14 | {{ end -}} 15 | 16 | {{- if .NoteGroups -}} 17 | {{ range .NoteGroups -}} 18 | ### {{ .Title }} 19 | {{ range .Notes }} 20 | {{ .Body }} 21 | {{ end }} 22 | {{ end -}} 23 | {{ end -}} 24 | 25 | {{ if .Tag.Previous }} 26 | ### All changes 27 | [`{{ .Tag.Previous.Name }}...{{ .Tag.Name }}`] 28 | {{ end }} 29 | 30 | {{ if .Tag.Previous -}} 31 | [`{{ .Tag.Previous.Name }}...{{ .Tag.Name }}`]: {{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }} 32 | {{ end -}} 33 | {{ end -}} 34 | -------------------------------------------------------------------------------- /.chglog/config.yml: -------------------------------------------------------------------------------- 1 | style: github 2 | template: CHANGELOG.tpl.md 3 | info: 4 | title: CHANGELOG 5 | repository_url: https://github.com/SAP/less-openui5 6 | options: 7 | commits: 8 | filters: 9 | Type: 10 | - FEATURE 11 | - FIX 12 | - PERF 13 | - DEPENDENCY 14 | - BREAKING 15 | commit_groups: 16 | title_maps: 17 | FEATURE: Features 18 | FIX: Bug Fixes 19 | PERF: Performance Improvements 20 | DEPENDENCY: Dependency Updates 21 | BREAKING: Breaking Changes 22 | header: 23 | pattern: "^\\[(\\w*)\\]\\s(?:([^\\:]*)\\:\\s)?(.*)$" 24 | pattern_maps: 25 | - Type 26 | - Scope 27 | - Subject 28 | issues: 29 | prefix: 30 | - "#" 31 | notes: 32 | keywords: 33 | - BREAKING CHANGE 34 | -------------------------------------------------------------------------------- /.chglog/release-config.yml: -------------------------------------------------------------------------------- 1 | style: github 2 | template: RELEASE.tpl.md 3 | info: 4 | repository_url: https://github.com/SAP/less-openui5 5 | options: 6 | commits: 7 | filters: 8 | Type: 9 | - FEATURE 10 | - FIX 11 | - PERF 12 | - DEPENDENCY 13 | - BREAKING 14 | commit_groups: 15 | title_maps: 16 | FEATURE: Features 17 | FIX: Bug Fixes 18 | PERF: Performance Improvements 19 | DEPENDENCY: Dependency Updates 20 | BREAKING: Breaking Changes 21 | header: 22 | pattern: "^\\[(\\w*)\\]\\s(?:([^\\:]*)\\:\\s)?(.*)$" 23 | pattern_maps: 24 | - Type 25 | - Scope 26 | - Subject 27 | issues: 28 | prefix: 29 | - "#" 30 | notes: 31 | keywords: 32 | - BREAKING CHANGE 33 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # see http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_style = tab 8 | 9 | [*.{css,html,js,less,txt,json,yml,md}] 10 | trim_trailing_whitespace = true 11 | end_of_line = lf 12 | indent_size = 4 13 | insert_final_newline = true 14 | 15 | [*.{yml,yaml}] 16 | indent_style = space 17 | indent_size = 2 18 | 19 | [*.md] 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | - package-ecosystem: npm 8 | directory: "/" 9 | schedule: 10 | interval: weekly 11 | day: sunday 12 | time: "10:00" 13 | timezone: Etc/UCT 14 | reviewers: 15 | - matz3 16 | - flovogt 17 | versioning-strategy: increase 18 | -------------------------------------------------------------------------------- /.github/workflows/dependabot-auto-merge.yml: -------------------------------------------------------------------------------- 1 | name: Dependabot auto-merge 2 | on: 3 | pull_request: 4 | branches: 5 | - main 6 | 7 | permissions: 8 | contents: write 9 | pull-requests: write 10 | 11 | jobs: 12 | dependabot: 13 | runs-on: ubuntu-latest 14 | if: ${{ github.actor == 'dependabot[bot]' && github.event.pull_request.auto_merge == null }} 15 | steps: 16 | - name: Dependabot metadata 17 | id: metadata 18 | uses: dependabot/fetch-metadata@v2 19 | with: 20 | github-token: "${{ secrets.GITHUB_TOKEN }}" 21 | - name: Approve and auto-merge PRs for minor/patch updates of github-actions 22 | if: | 23 | steps.metadata.outputs.package-ecosystem == 'github_actions' && 24 | contains(fromJSON('["version-update:semver-minor", "version-update:semver-patch"]'), steps.metadata.outputs.update-type) 25 | run: gh pr review --approve "$PR_URL" && gh pr merge --auto --rebase "$PR_URL" 26 | env: 27 | PR_URL: ${{github.event.pull_request.html_url}} 28 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 29 | -------------------------------------------------------------------------------- /.github/workflows/github-ci.yml: -------------------------------------------------------------------------------- 1 | name: GitHub CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | test: 11 | name: General checks, tests and coverage reporting 12 | runs-on: ubuntu-latest 13 | steps: 14 | 15 | - uses: actions/checkout@v4.2.2 16 | 17 | - name: Use Node.js LTS 20.11.0 18 | uses: actions/setup-node@v4.4.0 19 | with: 20 | node-version: 20.11.0 21 | 22 | - name: Install dependencies 23 | run: npm ci 24 | 25 | - name: Perform checks and tests 26 | run: npm test 27 | 28 | - name: Send report to Coveralls 29 | uses: coverallsapp/github-action@v2.3.6 30 | with: 31 | github-token: ${{ secrets.GITHUB_TOKEN }} 32 | 33 | test-matrix: 34 | name: Unit tests on Node.js ${{ matrix.node-version }} and ${{ matrix.os }} 35 | strategy: 36 | fail-fast: false 37 | matrix: 38 | node-version: [20, 22] 39 | os: [ubuntu-latest, windows-latest, macOS-latest] 40 | runs-on: ${{ matrix.os }} 41 | steps: 42 | 43 | - uses: actions/checkout@v4.2.2 44 | 45 | - name: Use Node.js ${{ matrix.node-version }} 46 | uses: actions/setup-node@v4.4.0 47 | with: 48 | node-version: ${{ matrix.node-version }} 49 | 50 | - run: npm ci 51 | name: Install dependencies 52 | 53 | - run: npm ls --prod 54 | name: Check for missing / extraneous Dependencies 55 | 56 | - run: npm run unit 57 | name: Run unit tests 58 | -------------------------------------------------------------------------------- /.github/workflows/reuse-compliance.yml: -------------------------------------------------------------------------------- 1 | name: REUSE 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | compliance-check: 11 | name: Compliance Check 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4.2.2 15 | - name: Execute REUSE Compliance Check 16 | uses: fsfe/reuse-action@v5 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # CI (Azure Pipelines) xUnit test results 21 | test-results.xml 22 | 23 | # IDEs 24 | .vscode/ 25 | *.~vsdx 26 | .idea/ 27 | 28 | # node-waf configuration 29 | .lock-wscript 30 | 31 | # Compiled binary addons (http://nodejs.org/api/addons.html) 32 | build/Release 33 | 34 | # Dependency directories 35 | node_modules 36 | jspm_packages 37 | 38 | # Optional npm cache directory 39 | .npm 40 | 41 | # Optional eslint cache 42 | .eslintcache 43 | 44 | # Optional REPL history 45 | .node_repl_history 46 | 47 | # Output of 'npm pack' 48 | *.tgz 49 | 50 | # Yarn Integrity file 51 | .yarn-integrity 52 | 53 | # Misc 54 | yarn.lock 55 | .DS_Store 56 | 57 | # Don't include private SSH key for deployment via Travis CI 58 | deploy_key 59 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | # Enforce public npm registry 2 | registry = https://registry.npmjs.org/ 3 | lockfile-version=3 -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to less-openui5 2 | 3 | In general the contributing guidelines of OpenUI5 also apply to this project. They can be found here: 4 | https://github.com/SAP/openui5/blob/-/CONTRIBUTING.md 5 | 6 | Some parts might not be relevant for this project (e.g. the browser-specific requirements like jQuery, CSS and accessibility in the "Contribution Content Guidelines") and the contribution process is easier (pull requests will be merged directly on GitHub). 7 | 8 | # Contributing with AI-generated code 9 | As artificial intelligence evolves, AI-generated code is becoming valuable for many software projects, including open-source initiatives. While we recognize the potential benefits of incorporating AI-generated content into our open-source projects there are certain requirements that need to be reflected and adhered to when making contributions. 10 | 11 | Please see our [guideline for AI-generated code contributions to SAP Open Source Software Projects](https://github.com/SAP/.github/blob/main/CONTRIBUTING_USING_GENAI.md) for these requirements. 12 | -------------------------------------------------------------------------------- /REUSE.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | SPDX-PackageName = "less-openui5" 3 | SPDX-PackageSupplier = "SAP OpenUI5 " 4 | SPDX-PackageDownloadLocation = "https://github.com/SAP/less-openui5" 5 | SPDX-PackageComment = "The code in this project may include calls to APIs (“API Calls”) of\n SAP or third-party products or services developed outside of this project\n (“External Products”).\n “APIs” means application programming interfaces, as well as their respective\n specifications and implementing code that allows software to communicate with\n other software.\n API Calls to External Products are not licensed under the open source license\n that governs this project. The use of such API Calls and related External\n Products are subject to applicable additional agreements with the relevant\n provider of the External Products. In no event shall the open source license\n that governs this project grant any rights in or to any External Products,or\n alter, expand or supersede any terms of the applicable additional agreements.\n If you have a valid license agreement with SAP for the use of a particular SAP\n External Product, then you may make use of any API Calls included in this\n project’s code for that SAP External Product, subject to the terms of such\n license agreement. If you do not have a valid license agreement for the use of\n a particular SAP External Product, then you may only make use of any API Calls\n in this project for that SAP External Product for your internal, non-productive\n and non-commercial test and evaluation of such API Calls. Nothing herein grants\n you any rights to use or access any SAP External Product, or provide any third\n parties the right to use of access any SAP External Product, through API Calls." 6 | 7 | [[annotations]] 8 | path = "**" 9 | precedence = "aggregate" 10 | SPDX-FileCopyrightText = "2025 SAP SE or an SAP affiliate company and less-openui5 contributors" 11 | SPDX-License-Identifier = "Apache-2.0" 12 | 13 | [[annotations]] 14 | path = "lib/thirdparty/less/**" 15 | precedence = "aggregate" 16 | SPDX-FileCopyrightText = "Copyright (c) 2009-2014 Alexis Sellier & The Core Less Team" 17 | SPDX-License-Identifier = "Apache-2.0" 18 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Reporting Security Issues 2 | 3 | We take security issues in our projects seriously. We appreciate your efforts to responsibly disclose your findings. 4 | 5 | Please do not report security issues directly on GitHub but using one of the channels listed below. This allows us to provide a fix before an issue can be exploited. 6 | 7 | - **Researchers/Non-SAP Customers:** Please consult SAPs [disclosure guidelines](https://wiki.scn.sap.com/wiki/display/PSR/Disclosure+Guidelines+for+SAP+Security+Advisories) and send the related information in a PGP encrypted e-mail to secure@sap.com. Find the public PGP key [here](https://www.sap.com/dmc/policies/pgp/keyblock.txt). 8 | - **SAP Customers:** If the security issue is not covered by a published security note, please report it by creating a customer message at https://launchpad.support.sap.com. 9 | 10 | Please also refer to the general [SAP security information page](https://www.sap.com/about/trust-center/security/incident-management.html). 11 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import jsdoc from "eslint-plugin-jsdoc"; 2 | import globals from "globals"; 3 | import js from "@eslint/js"; 4 | import google from "eslint-config-google"; 5 | 6 | 7 | export default [ 8 | js.configs.recommended, 9 | google, 10 | { 11 | ignores: ["lib/thirdparty/"], 12 | }, 13 | { 14 | plugins: { 15 | jsdoc, 16 | }, 17 | 18 | languageOptions: { 19 | globals: { 20 | ...globals.node, 21 | ...globals.mocha, 22 | }, 23 | 24 | ecmaVersion: 8, 25 | sourceType: "commonjs", 26 | }, 27 | 28 | settings: { 29 | jsdoc: { 30 | tagNamePreference: { 31 | return: "returns", 32 | }, 33 | }, 34 | }, 35 | 36 | rules: { 37 | "indent": ["error", "tab"], 38 | "linebreak-style": ["error", "unix"], 39 | 40 | "quotes": [ 41 | "error", 42 | "double", 43 | { 44 | allowTemplateLiterals: true, 45 | }, 46 | ], 47 | 48 | "semi": ["error", "always"], 49 | "no-negated-condition": "off", 50 | "require-jsdoc": "off", 51 | "no-mixed-requires": "off", 52 | 53 | "max-len": [ 54 | "warn", // TODO: set to "error" and fix all findings 55 | { 56 | code: 120, 57 | ignoreUrls: true, 58 | ignoreRegExpLiterals: true, 59 | }, 60 | ], 61 | 62 | "no-implicit-coercion": [ 63 | 2, 64 | { 65 | allow: ["!!"], 66 | }, 67 | ], 68 | 69 | "comma-dangle": "off", 70 | "no-tabs": "off", 71 | // This rule must be disabled as of ESLint 9. It's removed and causes issues when present. 72 | // https://eslint.org/docs/latest/rules/valid-jsdoc 73 | "valid-jsdoc": 0, 74 | "jsdoc/require-param-description": 0, 75 | "jsdoc/require-returns-description": 0, 76 | "jsdoc/require-returns": 0, 77 | }, 78 | }, 79 | { 80 | files: ["**/*.mjs"], 81 | 82 | languageOptions: { 83 | ecmaVersion: 2022, 84 | sourceType: "module", 85 | }, 86 | }, 87 | ]; 88 | -------------------------------------------------------------------------------- /lib/diff.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // Regular expression to match type of property in order to check whether 4 | // it possibly contains color values. 5 | const rProperties = /(color|background|border|text|outline)(?!-(width|radius|offset|style|align|overflow|transform))(-(color|shadow|image))?/; 6 | 7 | function selectorEquals(s1, s2) { 8 | // Make sure there is the same number of select parts 9 | if (s1.length !== s2.length) { 10 | return false; 11 | } 12 | 13 | // Check if all the parts are the same strings 14 | for (let i = 0; i < s1.length; i++) { 15 | if (s1[i] !== s2[i]) { 16 | return false; 17 | } 18 | } 19 | 20 | return true; 21 | } 22 | 23 | function Diffing(oBase, oCompare) { 24 | this.oBase = oBase; 25 | this.oCompare = oCompare; 26 | 27 | this.oDiff = { 28 | type: "stylesheet", 29 | stylesheet: { 30 | rules: [] 31 | }, 32 | }; 33 | 34 | this.oStack = { 35 | type: "stylesheet", 36 | stylesheet: { 37 | rules: [] 38 | }, 39 | }; 40 | } 41 | 42 | Diffing.prototype.diffRules = function(oBaseRules, oCompareRules) { 43 | const aDiffRules = []; 44 | let iBaseNode; let iCompareNode; 45 | 46 | for (iBaseNode = 0, iCompareNode = 0; iBaseNode < oBaseRules.length; iBaseNode++, iCompareNode++) { 47 | const oBaseNode = oBaseRules[iBaseNode]; 48 | let oCompareNode = oCompareRules[iCompareNode]; 49 | let oDiffNode = null; 50 | 51 | // Add all different compare nodes to stack and check for next one 52 | while (oCompareNode && oBaseNode.type !== oCompareNode.type) { 53 | this.oStack.stylesheet.rules.push(oCompareNode); 54 | iCompareNode++; 55 | oCompareNode = oCompareRules[iCompareNode]; 56 | } 57 | 58 | if (oCompareNode && oBaseNode.type === "comment") { 59 | const sBaseComment = oBaseNode.comment; 60 | const sCompareComment = oCompareNode.comment; 61 | 62 | if (sBaseComment !== sCompareComment) { 63 | oDiffNode = oCompareNode; 64 | } 65 | } 66 | 67 | if (oBaseNode.type === "rule") { 68 | // Add all rules with different selector to stack and check for next one 69 | while (oCompareNode && (oCompareNode.type !== "rule" || !selectorEquals(oBaseNode.selectors, oCompareNode.selectors))) { 70 | this.oStack.stylesheet.rules.push(oCompareNode); 71 | iCompareNode++; 72 | oCompareNode = oCompareRules[iCompareNode]; 73 | } 74 | 75 | const aBaseDeclarations = oBaseNode.declarations; 76 | const aCompareDeclarations = oCompareNode && oCompareNode.declarations; 77 | for (let j = 0; j < aBaseDeclarations.length; j++) { 78 | const oBaseDeclaration = aBaseDeclarations[j]; 79 | const oCompareDeclaration = aCompareDeclarations && aCompareDeclarations[j]; 80 | 81 | if (oCompareDeclaration && oBaseDeclaration.type === "declaration") { 82 | // TODO: Also check for different node and add to stack??? 83 | if (oBaseDeclaration.type === oCompareDeclaration.type) { 84 | if (oBaseDeclaration.property === oCompareDeclaration.property) { 85 | // Always add color properties to diff to prevent unexpected CSS overrides 86 | // due to selectors with more importance 87 | if (oBaseDeclaration.value !== oCompareDeclaration.value || 88 | oCompareDeclaration.property.match(rProperties)) { 89 | // Add compared rule to diff 90 | if (!oDiffNode) { 91 | oDiffNode = oCompareNode; 92 | oDiffNode.declarations = []; 93 | } 94 | oDiffNode.declarations.push(oCompareDeclaration); 95 | } 96 | } 97 | } 98 | } 99 | } 100 | } else if (oCompareNode && oBaseNode.type === "media") { 101 | const aMediaDiffRules = this.diffRules(oBaseNode.rules, oCompareNode.rules); 102 | 103 | if (aMediaDiffRules.length > 0) { 104 | oDiffNode = oCompareNode; 105 | oDiffNode.rules = aMediaDiffRules; 106 | } 107 | } 108 | 109 | if (oDiffNode) { 110 | aDiffRules.push(oDiffNode); 111 | } 112 | } 113 | 114 | // Add all leftover compare nodes to stack 115 | for (; iCompareNode < oCompareRules.length; iCompareNode++) { 116 | this.oStack.stylesheet.rules.push(oCompareRules[iCompareNode]); 117 | } 118 | 119 | return aDiffRules; 120 | }; 121 | 122 | Diffing.prototype.run = function() { 123 | const oBaseRules = this.oBase.stylesheet.rules; 124 | const oCompareRules = this.oCompare.stylesheet.rules; 125 | 126 | this.oDiff.stylesheet.rules = this.diffRules(oBaseRules, oCompareRules); 127 | 128 | return { 129 | diff: this.oDiff, 130 | stack: this.oStack 131 | }; 132 | }; 133 | 134 | 135 | module.exports = function diff(oBase, oCompare) { 136 | return new Diffing(oBase, oCompare).run(); 137 | }; 138 | -------------------------------------------------------------------------------- /lib/fileUtils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const path = require("path"); 4 | 5 | module.exports = function(fs) { 6 | if (!fs) { 7 | fs = require("fs"); 8 | } 9 | 10 | function statFile(filePath) { 11 | return new Promise(function(resolve, reject) { 12 | fs.stat(filePath, function(err, stat) { 13 | // No rejection here as it is ok if the file was not found 14 | resolve(stat ? {path: filePath, stat: stat} : null); 15 | }); 16 | }); 17 | } 18 | 19 | function statFiles(files) { 20 | return Promise.all(files.map(statFile)); 21 | } 22 | 23 | function findFile(filePath, rootPaths) { 24 | if (rootPaths && rootPaths.length > 0) { 25 | return statFiles( 26 | rootPaths.map(function(rootPath) { 27 | return path.join(rootPath, filePath); 28 | }) 29 | ).then(function(results) { 30 | for (let i = 0; i < results.length; i++) { 31 | if (results[i] !== null) { 32 | return results[i]; 33 | } 34 | } 35 | 36 | // File not found 37 | return null; 38 | }); 39 | } else { 40 | return statFile(filePath); 41 | } 42 | } 43 | 44 | function readFile(lessInputPath, rootPaths) { 45 | return findFile(lessInputPath, rootPaths).then(function(fileInfo) { 46 | if (!fileInfo) { 47 | return null; 48 | } 49 | return new Promise(function(resolve, reject) { 50 | fs.readFile(fileInfo.path, { 51 | encoding: "utf8" 52 | }, function(fileErr, content) { 53 | if (fileErr) { 54 | reject(fileErr); 55 | } else { 56 | resolve({ 57 | content: content, 58 | path: fileInfo.path, 59 | localPath: lessInputPath, 60 | stats: fileInfo.stats 61 | }); 62 | } 63 | }); 64 | }); 65 | }); 66 | } 67 | 68 | return { 69 | statFile: statFile, 70 | statFiles: statFiles, 71 | findFile: findFile, 72 | readFile: readFile 73 | }; 74 | }; 75 | -------------------------------------------------------------------------------- /lib/less/fileLoader.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file contains a modified version of the function "less.Parser.fileLoader" 3 | * from /lib/thirdparty/less/index.js (L127-L233). 4 | * Modifications are marked with comments in the code. 5 | */ 6 | 7 | "use strict"; 8 | 9 | module.exports = function createFileLoader(fileHandler) { 10 | return function fileLoader(file, currentFileInfo, callback, env) { 11 | /* BEGIN MODIFICATION */ 12 | 13 | // Removed unused variables "dirname, data" 14 | let pathname; 15 | 16 | 17 | const newFileInfo = { 18 | relativeUrls: env.relativeUrls, 19 | entryPath: currentFileInfo.entryPath, 20 | rootpath: currentFileInfo.rootpath, 21 | rootFilename: currentFileInfo.rootFilename 22 | }; 23 | 24 | /* END MODIFICATION */ 25 | 26 | function handleDataAndCallCallback(data) { 27 | const j = file.lastIndexOf("/"); 28 | 29 | // Pass on an updated rootpath if path of imported file is relative and file 30 | // is in a (sub|sup) directory 31 | // 32 | // Examples: 33 | // - If path of imported file is 'module/nav/nav.less' and rootpath is 'less/', 34 | // then rootpath should become 'less/module/nav/' 35 | // - If path of imported file is '../mixins.less' and rootpath is 'less/', 36 | // then rootpath should become 'less/../' 37 | if (newFileInfo.relativeUrls && !/^(?:[a-z-]+:|\/)/.test(file) && j != -1) { 38 | const relativeSubDirectory = file.slice(0, j+1); 39 | newFileInfo.rootpath = newFileInfo.rootpath + relativeSubDirectory; // append (sub|sup) directory path of imported file 40 | } 41 | // eslint-disable-next-line no-useless-escape 42 | newFileInfo.currentDirectory = pathname.replace(/[^\\\/]*$/, ""); 43 | newFileInfo.filename = pathname; 44 | 45 | callback(null, data, pathname, newFileInfo); 46 | } 47 | 48 | /* BEGIN MODIFICATION */ 49 | 50 | // Call custom function to handle file loading 51 | fileHandler(file, currentFileInfo, function(resolvedPathname, data) { 52 | pathname = resolvedPathname; 53 | handleDataAndCallCallback(data); 54 | }, callback); 55 | 56 | // The remainder of this function has been completely removed 57 | 58 | /* END MODIFICATION */ 59 | }; 60 | }; 61 | -------------------------------------------------------------------------------- /lib/less/importsPush.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file contains a modified version of the local function "imports.push" within the constructor of "less.Parser" 3 | * from /lib/thirdparty/less/parser.js (L70-L111) 4 | * Modifications are marked with comments in the code. 5 | */ 6 | 7 | /* eslint-disable consistent-this, new-cap, no-invalid-this */ 8 | "use strict"; 9 | 10 | const less = require("../thirdparty/less"); 11 | const tree = less.tree; 12 | 13 | module.exports = function createImportsPushFunction(env, fileLoader, parserFactory) { 14 | return function importsPush(path, currentFileInfo, importOptions, callback) { 15 | const parserImports = this; 16 | this.queue.push(path); 17 | 18 | const fileParsedFunc = function(e, root, fullPath) { 19 | parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue 20 | 21 | /* BEGIN MODIFICATION */ 22 | 23 | // Replaced "rootFilename" with "env.filename" 24 | const importedPreviously = fullPath === env.filename; 25 | 26 | /* END MODIFICATION */ 27 | 28 | parserImports.files[fullPath] = root; // Store the root 29 | 30 | if (e && !parserImports.error) { 31 | parserImports.error = e; 32 | } 33 | 34 | callback(e, root, importedPreviously, fullPath); 35 | }; 36 | 37 | if (less.Parser.importer) { 38 | less.Parser.importer(path, currentFileInfo, fileParsedFunc, env); 39 | } else { 40 | /* BEGIN MODIFICATION */ 41 | 42 | // Call custom fileLoader instead of "less.Parser.fileLoader" 43 | fileLoader(path, currentFileInfo, function(e, contents, fullPath, newFileInfo) { 44 | /* END MODIFICATION */ 45 | 46 | if (e) { 47 | fileParsedFunc(e); return; 48 | } 49 | 50 | const newEnv = new tree.parseEnv(env); 51 | 52 | newEnv.currentFileInfo = newFileInfo; 53 | newEnv.processImports = false; 54 | newEnv.contents[fullPath] = contents; 55 | 56 | if (currentFileInfo.reference || importOptions.reference) { 57 | newFileInfo.reference = true; 58 | } 59 | 60 | if (importOptions.inline) { 61 | fileParsedFunc(null, contents, fullPath); 62 | } else { 63 | /* BEGIN MODIFICATION */ 64 | 65 | // Create custom parser when resolving imports 66 | parserFactory(newEnv, fileLoader).parse(contents, function(e, root) { 67 | /* END MODIFICATION */ 68 | 69 | fileParsedFunc(e, root, fullPath); 70 | }); 71 | } 72 | }, env); 73 | } 74 | }; 75 | }; 76 | -------------------------------------------------------------------------------- /lib/less/parser.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable new-cap */ 2 | "use strict"; 3 | 4 | const less = require("../thirdparty/less"); 5 | const createFileLoader = require("./fileLoader"); 6 | const createImportsPushFunction = require("./importsPush"); 7 | 8 | function parserFactory(env, fileLoader) { 9 | // Make sure that provided "env" is an instance 10 | if (!(env instanceof less.tree.parseEnv)) { 11 | env = new less.tree.parseEnv(env); 12 | } 13 | 14 | const parser = new less.Parser(env); 15 | 16 | if (fileLoader) { 17 | patchParserImportsPush(parser, env, fileLoader); 18 | } 19 | 20 | return parser; 21 | } 22 | 23 | function patchParserImportsPush(parser, env, fileLoader) { 24 | // Hooks into the parser to be able to use custom file loader and use custom 25 | // parser factory for imports 26 | parser.imports.push = createImportsPushFunction(env, fileLoader, parserFactory); 27 | } 28 | 29 | module.exports = function createParser(env, fileHandler) { 30 | // Create fileLoader function if a fileHandler is provided 31 | let fileLoader; 32 | if (fileHandler) { 33 | fileLoader = createFileLoader(fileHandler); 34 | } 35 | 36 | return parserFactory(env, fileLoader); 37 | }; 38 | -------------------------------------------------------------------------------- /lib/plugin/css-variables-pointer.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const less = require("../thirdparty/less"); 4 | 5 | const CSSVariablesPointerPlugin = module.exports = function(config) { 6 | this.config = config; 7 | // eslint-disable-next-line new-cap 8 | this.native = new less.tree.visitor(this); 9 | this.ruleStack = []; 10 | this.callStack = []; 11 | this.mVars = {}; 12 | }; 13 | 14 | CSSVariablesPointerPlugin.prototype = { 15 | 16 | isPreEvalVisitor: true, 17 | isReplacing: true, 18 | 19 | run(root) { 20 | return this.native.visit(root); 21 | }, 22 | 23 | visitRule(node, visitArgs) { 24 | // store the rule context for the call variable extraction 25 | this.ruleStack.push(node); 26 | // replace the direct less variable assignment with the css variable 27 | if (Array.isArray(node.name) && node.name[0] instanceof less.tree.Keyword && this.mVars[node.name[0].value]) { 28 | node.value = new less.tree.Anonymous(this.mVars[node.name[0].value], node.index, node.currentFileInfo, node.mapLines); 29 | } 30 | return node; 31 | }, 32 | 33 | visitRuleOut(node) { 34 | // remove rule context 35 | this.ruleStack.pop(); 36 | return node; 37 | }, 38 | 39 | visitCall(node, visitArgs) { 40 | // store the call context for the call variable extraction 41 | this.callStack.push(node); 42 | return node; 43 | }, 44 | 45 | visitCallOut(node, visitArgs) { 46 | // remove call context 47 | this.callStack.pop(); 48 | return node; 49 | }, 50 | 51 | _isVariableAssignment(rule) { 52 | // determines a simple less variable assignment: 53 | // Rule > Value => Array[length=1] > Expression => Array[length=1] > Variable 54 | const value = rule && rule.variable && 55 | rule.value instanceof less.tree.Value && rule.value; 56 | const expression = value && 57 | Array.isArray(value.value) && value.value.length == 1 && 58 | value.value[0] instanceof less.tree.Expression && value.value[0]; 59 | const variable = expression && 60 | Array.isArray(expression.value) && expression.value.length == 1 && 61 | expression.value[0] instanceof less.tree.Variable && expression.value[0]; 62 | return variable; 63 | }, 64 | 65 | visitVariable(node, visitArgs) { 66 | // collect all simple less variables to less variables mapping (to convert them to css variables assignments) 67 | if (this.callStack.length == 0 && this.ruleStack.length > 0 && 68 | this._isVariableAssignment(this.ruleStack[this.ruleStack.length - 1])) { 69 | this.mVars["--" + this.ruleStack[0].name.substr(1)] = "var(" + node.name.replace(/^@/, "--") + ")"; 70 | } 71 | return node; 72 | } 73 | 74 | }; 75 | -------------------------------------------------------------------------------- /lib/plugin/import-collector.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const less = require("../thirdparty/less"); 4 | 5 | const ImportCollector = module.exports = function(options) { 6 | /* eslint-disable new-cap */ 7 | this.oVisitor = new less.tree.visitor(this); 8 | /* eslint-enable new-cap */ 9 | 10 | this.aImports = []; 11 | 12 | this.mImportMappings = options && options.importMappings ? options.importMappings : {}; 13 | }; 14 | 15 | ImportCollector.prototype = { 16 | isPreEvalVisitor: true, 17 | run: function(root) { 18 | this.oVisitor.visit(root); 19 | }, 20 | visitImport: function(importNode, visitArgs) { 21 | if (importNode.importedFilename) { 22 | const fullImportPath = this.mImportMappings[importNode.importedFilename]; 23 | if (fullImportPath) { 24 | this.aImports.push(fullImportPath); 25 | } else { 26 | this.aImports.push(importNode.importedFilename); 27 | } 28 | } 29 | }, 30 | getImports: function() { 31 | return this.aImports; 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /lib/plugin/url-collector.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const path = require("path"); 4 | const url = require("url"); 5 | const less = require("../thirdparty/less"); 6 | 7 | const urlNodeNames = { 8 | "background": true, 9 | "background-image": true, 10 | "content": true, 11 | "cursor": true, 12 | "icon": true, 13 | "list-style-image": true 14 | }; 15 | 16 | /** 17 | * @constructor 18 | */ 19 | const UrlCollectorPlugin = module.exports = function() { 20 | /* eslint-disable-next-line new-cap */ 21 | this.oVisitor = new less.tree.visitor(this); 22 | this.urls = new Map(); 23 | }; 24 | 25 | UrlCollectorPlugin.prototype = { 26 | isReplacing: false, 27 | isPreEvalVisitor: false, 28 | run: function(root) { 29 | return this.oVisitor.visit(root); 30 | }, 31 | visitRule: function(ruleNode) { 32 | if (urlNodeNames[ruleNode.name]) { 33 | this.visitUrl(ruleNode); 34 | } 35 | }, 36 | visitUrl: function(ruleNode) { 37 | for (const valueObject of ruleNode.value.value) { 38 | if (valueObject.type === "Url") { 39 | this.addUrlFromNode(valueObject); 40 | } else if (valueObject.type === "Expression") { 41 | for (const node of valueObject.value) { 42 | this.addUrlFromNode(node); 43 | } 44 | } 45 | } 46 | }, 47 | addUrlFromNode: function(node) { 48 | if (node.type === "Url") { 49 | const relativeUrl = node.value.value; 50 | 51 | const parsedUrl = url.parse(relativeUrl); 52 | // Ignore urls with protocol (also includes data urls) 53 | // Ignore server absolute urls 54 | if (parsedUrl.protocol || relativeUrl.startsWith("/")) { 55 | return; 56 | } 57 | 58 | const {currentDirectory} = node.currentFileInfo; 59 | 60 | const resolvedUrl = path.posix.join(currentDirectory, relativeUrl); 61 | this.urls.set(resolvedUrl, {currentDirectory, relativeUrl}); 62 | } 63 | }, 64 | getUrls: function() { 65 | return Array.from(this.urls.values()); 66 | } 67 | }; 68 | -------------------------------------------------------------------------------- /lib/plugin/variable-collector.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const posixPath = require("path").posix; 3 | const less = require("../thirdparty/less"); 4 | const backslashRegExp = /\\/g; 5 | 6 | function toPosix(p) { 7 | return p.replace(backslashRegExp, "/"); 8 | } 9 | 10 | const VariableCollector = module.exports = function(env) { 11 | /* eslint-disable new-cap */ 12 | this.oVisitor = new less.tree.visitor(this); 13 | /* eslint-enable new-cap */ 14 | this.env = env; 15 | this.mVariables = {}; 16 | this.mGlobalVariables = {}; 17 | this.mVarFile = {}; 18 | }; 19 | 20 | VariableCollector.prototype = { 21 | isPreVisitor: true, 22 | run: function(root) { 23 | this.mGlobalVariables = root.variables(); 24 | return this.oVisitor.visit(root); 25 | }, 26 | 27 | visitRule: function(node) { 28 | if (!node.variable) { 29 | return; 30 | } 31 | if (!this.mGlobalVariables[node.name]) { 32 | // Ignoring local variable 33 | return; 34 | } 35 | try { 36 | const value = node.value.toCSS(this.env); 37 | this.mVariables[node.name.substr(1)] = { 38 | value: value, 39 | filename: node.currentFileInfo.filename, 40 | rootFilename: node.currentFileInfo.rootFilename 41 | }; 42 | // eslint-disable-next-line no-unused-vars 43 | } catch (err) { 44 | // Errors might occur within mixins. 45 | // But as we only collect global variables, this doesn't matter... 46 | } 47 | }, 48 | 49 | getVariables: function(aImports) { 50 | const mVariables = {}; 51 | 52 | const aPosixImports = aImports.map(toPosix); 53 | 54 | const includeCache = {}; 55 | 56 | function shouldIncludeVariable({rootFilename, filename}) { 57 | // Simple case where variable is defined in root file 58 | if (rootFilename === filename) { 59 | return true; 60 | } 61 | 62 | const cacheKey = rootFilename + "|" + filename; 63 | if (includeCache[cacheKey] !== undefined) { 64 | return includeCache[cacheKey]; 65 | } 66 | 67 | const dirname = posixPath.dirname(filename); 68 | const baseFileName = posixPath.basename(rootFilename); // usually library.source.less 69 | 70 | function check(currentDirname) { 71 | const libraryBaseFile = posixPath.join(currentDirname, baseFileName); 72 | if (libraryBaseFile === rootFilename || aPosixImports.includes(libraryBaseFile)) { 73 | return true; 74 | } else { 75 | // Recursively check parent directories whether they contain an imported "base file" 76 | // This is only relevant when a theme contains sub-directories 77 | const parentDirname = posixPath.dirname(currentDirname); 78 | if (parentDirname === currentDirname) { 79 | return false; // We are at the root 80 | } else { 81 | return check(parentDirname); 82 | } 83 | } 84 | } 85 | 86 | return includeCache[cacheKey] = check(dirname); 87 | } 88 | 89 | for (const name in this.mVariables) { 90 | if (Object.prototype.hasOwnProperty.call(this.mVariables, name)) { 91 | const oVar = this.mVariables[name]; 92 | // Ensure posix paths 93 | const rootFilename = toPosix(oVar.rootFilename); 94 | const filename = toPosix(oVar.filename); 95 | 96 | // Only add variable if the corresponding library "base file" has been imported 97 | if (shouldIncludeVariable({rootFilename, filename})) { 98 | mVariables[name] = oVar.value; 99 | } 100 | } 101 | } 102 | 103 | return mVariables; 104 | }, 105 | 106 | getAllVariables: function(aImports) { 107 | const mVariables = {}; 108 | for (const name in this.mVariables) { 109 | if (Object.prototype.hasOwnProperty.call(this.mVariables, name)) { 110 | const oVar = this.mVariables[name]; 111 | mVariables[name] = oVar.value; 112 | } 113 | } 114 | return mVariables; 115 | } 116 | 117 | }; 118 | -------------------------------------------------------------------------------- /lib/scope.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * DOM-Elements like html, body as CSS selector needs be handled differently in 5 | * the scoping. Those selectors will be identified by matching the respective 6 | * regEx. 7 | */ 8 | const rRegex = /(html[^\s]*|body[^\s]*)/; 9 | 10 | const rPoint = /(^\s?\.{1}\w*)/; 11 | 12 | function handleScoping(sSelector, sScopeName) { 13 | /** 14 | * Match the selector to regex, by splitting into two array elements, 15 | * where the first element is the resulting matching group. 16 | */ 17 | const aCaptureGroups = sSelector.split(rRegex); 18 | 19 | const aSelectors = []; 20 | let sSelector1; let sSelector2; 21 | 22 | // filter empty strings and undefined objects 23 | const aMatch = aCaptureGroups.filter(function(n) { 24 | return !!n; 25 | }); 26 | 27 | if (aMatch.length > 1) { 28 | // set scope name after matching group. 29 | sSelector1 = aMatch[0] + " " + sScopeName + (aMatch[1] || ""); 30 | 31 | // match rPoint to check if following capture group 32 | // starts with a css class or dom element 33 | if (aMatch[1].match(rPoint)) { 34 | sSelector2 = aMatch[0] + " " + sScopeName + 35 | aMatch[1].replace(/\s/, ""); 36 | } else { 37 | // no match, selector is a dom element 38 | sSelector2 = null; 39 | } 40 | } else { 41 | // match if capture group starts with css rule 42 | if (aMatch[0].match(rPoint)) { 43 | // set scope before selector 44 | sSelector1 = sScopeName + aMatch[0]; 45 | sSelector2 = sScopeName + " " + aMatch[0]; 46 | } else { 47 | if (aMatch[0] === ":root") { 48 | // remove the :root scope 49 | sSelector1 = sScopeName; 50 | sSelector2 = null; 51 | } else if (aMatch[0].match(rRegex)) { 52 | // selector matches custom css rule 53 | sSelector1 = aMatch[0] + sScopeName; 54 | sSelector2 = aMatch[0] + " " + sScopeName; 55 | } else { 56 | // DOM element, add space 57 | sSelector1 = sScopeName + " " + aMatch[0]; 58 | sSelector2 = null; 59 | } 60 | } 61 | } 62 | 63 | aSelectors.push(sSelector1); 64 | aSelectors.push(sSelector2); 65 | 66 | return aSelectors; 67 | } 68 | 69 | function Scoping(oSheet, sScopeName) { 70 | this.oSheet = oSheet; 71 | this.sScopeName = sScopeName; 72 | } 73 | 74 | Scoping.prototype.scopeRules = function(oRules) { 75 | for (let iNode = 0; iNode < oRules.length; iNode++) { 76 | const oNode = oRules[iNode]; 77 | 78 | if (oNode.type === "rule") { 79 | const aNewSelectors = []; 80 | 81 | for (let i = 0; i < oNode.selectors.length; i++) { 82 | let sSelector = oNode.selectors[i]; 83 | let sSelector2; 84 | 85 | if (!(sSelector.match(/.sapContrast/))) { 86 | const aScopedSelectors = handleScoping(sSelector, this.sScopeName); 87 | sSelector = (aScopedSelectors[0] ? aScopedSelectors[0] : sSelector); 88 | sSelector2 = (aScopedSelectors[1] ? aScopedSelectors[1] : null); 89 | 90 | aNewSelectors.push(sSelector); 91 | if (sSelector2) { 92 | aNewSelectors.push(sSelector2); 93 | } 94 | } else { 95 | // scope name already exists 96 | aNewSelectors.push(sSelector); 97 | } 98 | } 99 | 100 | if (aNewSelectors.length > 0) { 101 | oNode.selectors = aNewSelectors; 102 | } 103 | } else if (oNode.type === "media") { 104 | this.scopeRules(oNode.rules); 105 | } 106 | } 107 | }; 108 | 109 | Scoping.prototype.run = function() { 110 | this.scopeRules(this.oSheet.stylesheet.rules); 111 | return this.oSheet; 112 | }; 113 | 114 | 115 | module.exports = function scope(oSheet, sScopeName) { 116 | return new Scoping(oSheet, sScopeName).run(); 117 | }; 118 | 119 | module.exports.scopeCssRoot = function scopeCssRoot(oRules, sScopeName) { 120 | for (let iNode = 0; iNode < oRules.length; iNode++) { 121 | const oNode = oRules[iNode]; 122 | 123 | if (oNode.type === "rule") { 124 | for (let i = 0; i < oNode.selectors.length; i++) { 125 | const sSelector = oNode.selectors[i]; 126 | 127 | if (sSelector.match(/#CSS_SCOPE_ROOT\b/)) { 128 | oNode.selectors[i] = "." + sScopeName; 129 | 130 | oRules.splice(iNode, 1); 131 | 132 | return oNode; 133 | } 134 | } 135 | } 136 | } 137 | }; 138 | -------------------------------------------------------------------------------- /lib/themingParameters/dataUri.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | addInlineParameters: function({result, options}) { 3 | // Inline parameters can only be added when the library name is known 4 | if (typeof options.library !== "object" || typeof options.library.name !== "string") { 5 | return result; 6 | } 7 | 8 | const parameters = JSON.stringify(result.variables); 9 | 10 | // properly escape the parameters to be part of a data-uri 11 | // + escaping single quote (') as it is used to surround the data-uri: url('...') 12 | const escapedParameters = encodeURIComponent(parameters).replace(/'/g, function(char) { 13 | return escape(char); 14 | }); 15 | 16 | // embed parameter variables as plain-text string into css 17 | const parameterStyleRule = "\n/* Inline theming parameters */\n#sap-ui-theme-" + 18 | options.library.name.replace(/\./g, "\\.") + 19 | "{background-image:url('data:text/plain;utf-8," + escapedParameters + "')}\n"; 20 | 21 | // embed parameter variables as plain-text string into css 22 | result.css += parameterStyleRule; 23 | if (options.rtl) { 24 | result.cssRtl += parameterStyleRule; 25 | } 26 | if (options.cssVariables) { 27 | // for the css variables build we just add it to the variables 28 | result.cssVariables += parameterStyleRule; 29 | } 30 | 31 | return result; 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /lib/thirdparty/less/README.md: -------------------------------------------------------------------------------- 1 | # less.js 2 | 3 | This folder contains the `lib/less` sub-folder of [v1.6.3](https://github.com/less/less.js/tree/v1.6.3/lib/less) of the [less.js project](https://github.com/less/less.js) with commit [`ccd8ebbfdfa300b6e748e8d7c12e3dbb0efd8371`](https://github.com/less/less.js/commit/ccd8ebbfdfa300b6e748e8d7c12e3dbb0efd8371) applied on top of it to resolve https://github.com/SAP/less-openui5/issues/24. 4 | 5 | The files `browser.js` and `rhino.js` have been removed, as they are not relevant for the Node.js implementation. 6 | 7 | The file `tree/javascript.js` has been removed to disable JavaScript execution. 8 | 9 | Modifications within the files are marked with `/* BEGIN MODIFICATION */` and `/* END MODIFICATION */` comments. 10 | -------------------------------------------------------------------------------- /lib/thirdparty/less/encoder.js: -------------------------------------------------------------------------------- 1 | // base64 encoder implementation for node 2 | exports.encodeBase64 = function(str) { 3 | return new Buffer(str).toString('base64'); 4 | }; 5 | -------------------------------------------------------------------------------- /lib/thirdparty/less/join-selector-visitor.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | tree.joinSelectorVisitor = function() { 3 | this.contexts = [[]]; 4 | this._visitor = new tree.visitor(this); 5 | }; 6 | 7 | tree.joinSelectorVisitor.prototype = { 8 | run: function (root) { 9 | return this._visitor.visit(root); 10 | }, 11 | visitRule: function (ruleNode, visitArgs) { 12 | visitArgs.visitDeeper = false; 13 | }, 14 | visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { 15 | visitArgs.visitDeeper = false; 16 | }, 17 | 18 | visitRuleset: function (rulesetNode, visitArgs) { 19 | var context = this.contexts[this.contexts.length - 1], 20 | paths = [], selectors; 21 | 22 | this.contexts.push(paths); 23 | 24 | if (! rulesetNode.root) { 25 | selectors = rulesetNode.selectors; 26 | if (selectors) { 27 | selectors = selectors.filter(function(selector) { return selector.getIsOutput(); }); 28 | rulesetNode.selectors = selectors.length ? selectors : (selectors = null); 29 | if (selectors) { rulesetNode.joinSelectors(paths, context, selectors); } 30 | } 31 | if (!selectors) { rulesetNode.rules = null; } 32 | rulesetNode.paths = paths; 33 | } 34 | }, 35 | visitRulesetOut: function (rulesetNode) { 36 | this.contexts.length = this.contexts.length - 1; 37 | }, 38 | visitMedia: function (mediaNode, visitArgs) { 39 | var context = this.contexts[this.contexts.length - 1]; 40 | mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia); 41 | } 42 | }; 43 | 44 | })(require('./tree')); -------------------------------------------------------------------------------- /lib/thirdparty/less/tree.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.debugInfo = function(env, ctx, lineSeperator) { 4 | var result=""; 5 | if (env.dumpLineNumbers && !env.compress) { 6 | switch(env.dumpLineNumbers) { 7 | case 'comments': 8 | result = tree.debugInfo.asComment(ctx); 9 | break; 10 | case 'mediaquery': 11 | result = tree.debugInfo.asMediaQuery(ctx); 12 | break; 13 | case 'all': 14 | result = tree.debugInfo.asComment(ctx) + (lineSeperator || "") + tree.debugInfo.asMediaQuery(ctx); 15 | break; 16 | } 17 | } 18 | return result; 19 | }; 20 | 21 | tree.debugInfo.asComment = function(ctx) { 22 | return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; 23 | }; 24 | 25 | tree.debugInfo.asMediaQuery = function(ctx) { 26 | return '@media -sass-debug-info{filename{font-family:' + 27 | ('file://' + ctx.debugInfo.fileName).replace(/([.:\/\\])/g, function (a) { 28 | if (a == '\\') { 29 | a = '\/'; 30 | } 31 | return '\\' + a; 32 | }) + 33 | '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; 34 | }; 35 | 36 | tree.find = function (obj, fun) { 37 | for (var i = 0, r; i < obj.length; i++) { 38 | r = fun.call(obj, obj[i]); 39 | if (r) { return r; } 40 | } 41 | return null; 42 | }; 43 | 44 | tree.jsify = function (obj) { 45 | if (Array.isArray(obj.value) && (obj.value.length > 1)) { 46 | return '[' + obj.value.map(function (v) { return v.toCSS(false); }).join(', ') + ']'; 47 | } else { 48 | return obj.toCSS(false); 49 | } 50 | }; 51 | 52 | tree.toCSS = function (env) { 53 | var strs = []; 54 | this.genCSS(env, { 55 | add: function(chunk, fileInfo, index) { 56 | strs.push(chunk); 57 | }, 58 | isEmpty: function () { 59 | return strs.length === 0; 60 | } 61 | }); 62 | return strs.join(''); 63 | }; 64 | 65 | tree.outputRuleset = function (env, output, rules) { 66 | var ruleCnt = rules.length, i; 67 | env.tabLevel = (env.tabLevel | 0) + 1; 68 | 69 | // Compressed 70 | if (env.compress) { 71 | output.add('{'); 72 | for (i = 0; i < ruleCnt; i++) { 73 | rules[i].genCSS(env, output); 74 | } 75 | output.add('}'); 76 | env.tabLevel--; 77 | return; 78 | } 79 | 80 | // Non-compressed 81 | var tabSetStr = '\n' + Array(env.tabLevel).join(" "), tabRuleStr = tabSetStr + " "; 82 | if (!ruleCnt) { 83 | output.add(" {" + tabSetStr + '}'); 84 | } else { 85 | output.add(" {" + tabRuleStr); 86 | rules[0].genCSS(env, output); 87 | for (i = 1; i < ruleCnt; i++) { 88 | output.add(tabRuleStr); 89 | rules[i].genCSS(env, output); 90 | } 91 | output.add(tabSetStr + '}'); 92 | } 93 | 94 | env.tabLevel--; 95 | }; 96 | 97 | })(require('./tree')); 98 | -------------------------------------------------------------------------------- /lib/thirdparty/less/tree/alpha.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Alpha = function (val) { 4 | this.value = val; 5 | }; 6 | tree.Alpha.prototype = { 7 | type: "Alpha", 8 | accept: function (visitor) { 9 | this.value = visitor.visit(this.value); 10 | }, 11 | eval: function (env) { 12 | if (this.value.eval) { return new tree.Alpha(this.value.eval(env)); } 13 | return this; 14 | }, 15 | genCSS: function (env, output) { 16 | output.add("alpha(opacity="); 17 | 18 | if (this.value.genCSS) { 19 | this.value.genCSS(env, output); 20 | } else { 21 | output.add(this.value); 22 | } 23 | 24 | output.add(")"); 25 | }, 26 | toCSS: tree.toCSS 27 | }; 28 | 29 | })(require('../tree')); 30 | -------------------------------------------------------------------------------- /lib/thirdparty/less/tree/anonymous.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Anonymous = function (string, index, currentFileInfo, mapLines) { 4 | this.value = string.value || string; 5 | this.index = index; 6 | this.mapLines = mapLines; 7 | this.currentFileInfo = currentFileInfo; 8 | }; 9 | tree.Anonymous.prototype = { 10 | type: "Anonymous", 11 | eval: function () { 12 | return new tree.Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines); 13 | }, 14 | compare: function (x) { 15 | if (!x.toCSS) { 16 | return -1; 17 | } 18 | 19 | var left = this.toCSS(), 20 | right = x.toCSS(); 21 | 22 | if (left === right) { 23 | return 0; 24 | } 25 | 26 | return left < right ? -1 : 1; 27 | }, 28 | genCSS: function (env, output) { 29 | output.add(this.value, this.currentFileInfo, this.index, this.mapLines); 30 | }, 31 | toCSS: tree.toCSS 32 | }; 33 | 34 | })(require('../tree')); 35 | -------------------------------------------------------------------------------- /lib/thirdparty/less/tree/assignment.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Assignment = function (key, val) { 4 | this.key = key; 5 | this.value = val; 6 | }; 7 | tree.Assignment.prototype = { 8 | type: "Assignment", 9 | accept: function (visitor) { 10 | this.value = visitor.visit(this.value); 11 | }, 12 | eval: function (env) { 13 | if (this.value.eval) { 14 | return new(tree.Assignment)(this.key, this.value.eval(env)); 15 | } 16 | return this; 17 | }, 18 | genCSS: function (env, output) { 19 | output.add(this.key + '='); 20 | if (this.value.genCSS) { 21 | this.value.genCSS(env, output); 22 | } else { 23 | output.add(this.value); 24 | } 25 | }, 26 | toCSS: tree.toCSS 27 | }; 28 | 29 | })(require('../tree')); 30 | -------------------------------------------------------------------------------- /lib/thirdparty/less/tree/call.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | // 4 | // A function call node. 5 | // 6 | tree.Call = function (name, args, index, currentFileInfo) { 7 | this.name = name; 8 | this.args = args; 9 | this.index = index; 10 | this.currentFileInfo = currentFileInfo; 11 | }; 12 | tree.Call.prototype = { 13 | type: "Call", 14 | accept: function (visitor) { 15 | if (this.args) { 16 | this.args = visitor.visitArray(this.args); 17 | } 18 | }, 19 | // 20 | // When evaluating a function call, 21 | // we either find the function in `tree.functions` [1], 22 | // in which case we call it, passing the evaluated arguments, 23 | // if this returns null or we cannot find the function, we 24 | // simply print it out as it appeared originally [2]. 25 | // 26 | // The *functions.js* file contains the built-in functions. 27 | // 28 | // The reason why we evaluate the arguments, is in the case where 29 | // we try to pass a variable to a function, like: `saturate(@color)`. 30 | // The function should receive the value, not the variable. 31 | // 32 | eval: function (env) { 33 | var args = this.args.map(function (a) { return a.eval(env); }), 34 | nameLC = this.name.toLowerCase(), 35 | result, func; 36 | 37 | if (nameLC in tree.functions) { // 1. 38 | try { 39 | func = new tree.functionCall(env, this.currentFileInfo); 40 | result = func[nameLC].apply(func, args); 41 | if (result != null) { 42 | return result; 43 | } 44 | } catch (e) { 45 | throw { type: e.type || "Runtime", 46 | message: "error evaluating function `" + this.name + "`" + 47 | (e.message ? ': ' + e.message : ''), 48 | index: this.index, filename: this.currentFileInfo.filename }; 49 | } 50 | } 51 | 52 | return new tree.Call(this.name, args, this.index, this.currentFileInfo); 53 | }, 54 | 55 | genCSS: function (env, output) { 56 | output.add(this.name + "(", this.currentFileInfo, this.index); 57 | 58 | for(var i = 0; i < this.args.length; i++) { 59 | this.args[i].genCSS(env, output); 60 | if (i + 1 < this.args.length) { 61 | output.add(", "); 62 | } 63 | } 64 | 65 | output.add(")"); 66 | }, 67 | 68 | toCSS: tree.toCSS 69 | }; 70 | 71 | })(require('../tree')); 72 | -------------------------------------------------------------------------------- /lib/thirdparty/less/tree/comment.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Comment = function (value, silent, index, currentFileInfo) { 4 | this.value = value; 5 | this.silent = !!silent; 6 | this.currentFileInfo = currentFileInfo; 7 | }; 8 | tree.Comment.prototype = { 9 | type: "Comment", 10 | genCSS: function (env, output) { 11 | if (this.debugInfo) { 12 | output.add(tree.debugInfo(env, this), this.currentFileInfo, this.index); 13 | } 14 | output.add(this.value.trim()); //TODO shouldn't need to trim, we shouldn't grab the \n 15 | }, 16 | toCSS: tree.toCSS, 17 | isSilent: function(env) { 18 | var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced), 19 | isCompressed = env.compress && !this.value.match(/^\/\*!/); 20 | return this.silent || isReference || isCompressed; 21 | }, 22 | eval: function () { return this; }, 23 | markReferenced: function () { 24 | this.isReferenced = true; 25 | } 26 | }; 27 | 28 | })(require('../tree')); 29 | -------------------------------------------------------------------------------- /lib/thirdparty/less/tree/condition.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Condition = function (op, l, r, i, negate) { 4 | this.op = op.trim(); 5 | this.lvalue = l; 6 | this.rvalue = r; 7 | this.index = i; 8 | this.negate = negate; 9 | }; 10 | tree.Condition.prototype = { 11 | type: "Condition", 12 | accept: function (visitor) { 13 | this.lvalue = visitor.visit(this.lvalue); 14 | this.rvalue = visitor.visit(this.rvalue); 15 | }, 16 | eval: function (env) { 17 | var a = this.lvalue.eval(env), 18 | b = this.rvalue.eval(env); 19 | 20 | var i = this.index, result; 21 | 22 | result = (function (op) { 23 | switch (op) { 24 | case 'and': 25 | return a && b; 26 | case 'or': 27 | return a || b; 28 | default: 29 | if (a.compare) { 30 | result = a.compare(b); 31 | } else if (b.compare) { 32 | result = b.compare(a); 33 | } else { 34 | throw { type: "Type", 35 | message: "Unable to perform comparison", 36 | index: i }; 37 | } 38 | switch (result) { 39 | case -1: return op === '<' || op === '=<' || op === '<='; 40 | case 0: return op === '=' || op === '>=' || op === '=<' || op === '<='; 41 | case 1: return op === '>' || op === '>='; 42 | } 43 | } 44 | })(this.op); 45 | return this.negate ? !result : result; 46 | } 47 | }; 48 | 49 | })(require('../tree')); 50 | -------------------------------------------------------------------------------- /lib/thirdparty/less/tree/directive.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Directive = function (name, value, index, currentFileInfo) { 4 | this.name = name; 5 | 6 | if (Array.isArray(value)) { 7 | this.rules = [new(tree.Ruleset)(null, value)]; 8 | this.rules[0].allowImports = true; 9 | } else { 10 | this.value = value; 11 | } 12 | this.index = index; 13 | this.currentFileInfo = currentFileInfo; 14 | 15 | }; 16 | tree.Directive.prototype = { 17 | type: "Directive", 18 | accept: function (visitor) { 19 | if (this.rules) { 20 | this.rules = visitor.visitArray(this.rules); 21 | } 22 | if (this.value) { 23 | this.value = visitor.visit(this.value); 24 | } 25 | }, 26 | genCSS: function (env, output) { 27 | output.add(this.name, this.currentFileInfo, this.index); 28 | if (this.rules) { 29 | tree.outputRuleset(env, output, this.rules); 30 | } else { 31 | output.add(' '); 32 | this.value.genCSS(env, output); 33 | output.add(';'); 34 | } 35 | }, 36 | toCSS: tree.toCSS, 37 | eval: function (env) { 38 | var evaldDirective = this; 39 | if (this.rules) { 40 | env.frames.unshift(this); 41 | evaldDirective = new(tree.Directive)(this.name, null, this.index, this.currentFileInfo); 42 | evaldDirective.rules = [this.rules[0].eval(env)]; 43 | evaldDirective.rules[0].root = true; 44 | env.frames.shift(); 45 | } 46 | return evaldDirective; 47 | }, 48 | variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, 49 | find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, 50 | rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, 51 | markReferenced: function () { 52 | var i, rules; 53 | this.isReferenced = true; 54 | if (this.rules) { 55 | rules = this.rules[0].rules; 56 | for (i = 0; i < rules.length; i++) { 57 | if (rules[i].markReferenced) { 58 | rules[i].markReferenced(); 59 | } 60 | } 61 | } 62 | } 63 | }; 64 | 65 | })(require('../tree')); 66 | -------------------------------------------------------------------------------- /lib/thirdparty/less/tree/element.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Element = function (combinator, value, index, currentFileInfo) { 4 | this.combinator = combinator instanceof tree.Combinator ? 5 | combinator : new(tree.Combinator)(combinator); 6 | 7 | if (typeof(value) === 'string') { 8 | this.value = value.trim(); 9 | } else if (value) { 10 | this.value = value; 11 | } else { 12 | this.value = ""; 13 | } 14 | this.index = index; 15 | this.currentFileInfo = currentFileInfo; 16 | }; 17 | tree.Element.prototype = { 18 | type: "Element", 19 | accept: function (visitor) { 20 | var value = this.value; 21 | this.combinator = visitor.visit(this.combinator); 22 | if (typeof value === "object") { 23 | this.value = visitor.visit(value); 24 | } 25 | }, 26 | eval: function (env) { 27 | return new(tree.Element)(this.combinator, 28 | this.value.eval ? this.value.eval(env) : this.value, 29 | this.index, 30 | this.currentFileInfo); 31 | }, 32 | genCSS: function (env, output) { 33 | output.add(this.toCSS(env), this.currentFileInfo, this.index); 34 | }, 35 | toCSS: function (env) { 36 | var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); 37 | if (value === '' && this.combinator.value.charAt(0) === '&') { 38 | return ''; 39 | } else { 40 | return this.combinator.toCSS(env || {}) + value; 41 | } 42 | } 43 | }; 44 | 45 | tree.Attribute = function (key, op, value) { 46 | this.key = key; 47 | this.op = op; 48 | this.value = value; 49 | }; 50 | tree.Attribute.prototype = { 51 | type: "Attribute", 52 | eval: function (env) { 53 | return new(tree.Attribute)(this.key.eval ? this.key.eval(env) : this.key, 54 | this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); 55 | }, 56 | genCSS: function (env, output) { 57 | output.add(this.toCSS(env)); 58 | }, 59 | toCSS: function (env) { 60 | var value = this.key.toCSS ? this.key.toCSS(env) : this.key; 61 | 62 | if (this.op) { 63 | value += this.op; 64 | value += (this.value.toCSS ? this.value.toCSS(env) : this.value); 65 | } 66 | 67 | return '[' + value + ']'; 68 | } 69 | }; 70 | 71 | tree.Combinator = function (value) { 72 | if (value === ' ') { 73 | this.value = ' '; 74 | } else { 75 | this.value = value ? value.trim() : ""; 76 | } 77 | }; 78 | tree.Combinator.prototype = { 79 | type: "Combinator", 80 | _outputMap: { 81 | '' : '', 82 | ' ' : ' ', 83 | ':' : ' :', 84 | '+' : ' + ', 85 | '~' : ' ~ ', 86 | '>' : ' > ', 87 | '|' : '|', 88 | '^' : ' ^ ', 89 | '^^' : ' ^^ ' 90 | }, 91 | _outputMapCompressed: { 92 | '' : '', 93 | ' ' : ' ', 94 | ':' : ' :', 95 | '+' : '+', 96 | '~' : '~', 97 | '>' : '>', 98 | '|' : '|', 99 | '^' : '^', 100 | '^^' : '^^' 101 | }, 102 | genCSS: function (env, output) { 103 | output.add((env.compress ? this._outputMapCompressed : this._outputMap)[this.value]); 104 | }, 105 | toCSS: tree.toCSS 106 | }; 107 | 108 | })(require('../tree')); 109 | -------------------------------------------------------------------------------- /lib/thirdparty/less/tree/expression.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Expression = function (value) { this.value = value; }; 4 | tree.Expression.prototype = { 5 | type: "Expression", 6 | accept: function (visitor) { 7 | if (this.value) { 8 | this.value = visitor.visitArray(this.value); 9 | } 10 | }, 11 | eval: function (env) { 12 | var returnValue, 13 | inParenthesis = this.parens && !this.parensInOp, 14 | doubleParen = false; 15 | if (inParenthesis) { 16 | env.inParenthesis(); 17 | } 18 | if (this.value.length > 1) { 19 | returnValue = new(tree.Expression)(this.value.map(function (e) { 20 | return e.eval(env); 21 | })); 22 | } else if (this.value.length === 1) { 23 | if (this.value[0].parens && !this.value[0].parensInOp) { 24 | doubleParen = true; 25 | } 26 | returnValue = this.value[0].eval(env); 27 | } else { 28 | returnValue = this; 29 | } 30 | if (inParenthesis) { 31 | env.outOfParenthesis(); 32 | } 33 | if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) { 34 | returnValue = new(tree.Paren)(returnValue); 35 | } 36 | return returnValue; 37 | }, 38 | genCSS: function (env, output) { 39 | for(var i = 0; i < this.value.length; i++) { 40 | this.value[i].genCSS(env, output); 41 | if (i + 1 < this.value.length) { 42 | output.add(" "); 43 | } 44 | } 45 | }, 46 | toCSS: tree.toCSS, 47 | throwAwayComments: function () { 48 | this.value = this.value.filter(function(v) { 49 | return !(v instanceof tree.Comment); 50 | }); 51 | } 52 | }; 53 | 54 | })(require('../tree')); 55 | -------------------------------------------------------------------------------- /lib/thirdparty/less/tree/extend.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Extend = function Extend(selector, option, index) { 4 | this.selector = selector; 5 | this.option = option; 6 | this.index = index; 7 | this.object_id = tree.Extend.next_id++; 8 | this.parent_ids = [this.object_id]; 9 | 10 | switch(option) { 11 | case "all": 12 | this.allowBefore = true; 13 | this.allowAfter = true; 14 | break; 15 | default: 16 | this.allowBefore = false; 17 | this.allowAfter = false; 18 | break; 19 | } 20 | }; 21 | tree.Extend.next_id = 0; 22 | 23 | tree.Extend.prototype = { 24 | type: "Extend", 25 | accept: function (visitor) { 26 | this.selector = visitor.visit(this.selector); 27 | }, 28 | eval: function (env) { 29 | return new(tree.Extend)(this.selector.eval(env), this.option, this.index); 30 | }, 31 | clone: function (env) { 32 | return new(tree.Extend)(this.selector, this.option, this.index); 33 | }, 34 | findSelfSelectors: function (selectors) { 35 | var selfElements = [], 36 | i, 37 | selectorElements; 38 | 39 | for(i = 0; i < selectors.length; i++) { 40 | selectorElements = selectors[i].elements; 41 | // duplicate the logic in genCSS function inside the selector node. 42 | // future TODO - move both logics into the selector joiner visitor 43 | if (i > 0 && selectorElements.length && selectorElements[0].combinator.value === "") { 44 | selectorElements[0].combinator.value = ' '; 45 | } 46 | selfElements = selfElements.concat(selectors[i].elements); 47 | } 48 | 49 | this.selfSelectors = [{ elements: selfElements }]; 50 | } 51 | }; 52 | 53 | })(require('../tree')); 54 | -------------------------------------------------------------------------------- /lib/thirdparty/less/tree/keyword.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Keyword = function (value) { this.value = value; }; 4 | tree.Keyword.prototype = { 5 | type: "Keyword", 6 | eval: function () { return this; }, 7 | genCSS: function (env, output) { 8 | output.add(this.value); 9 | }, 10 | toCSS: tree.toCSS, 11 | compare: function (other) { 12 | if (other instanceof tree.Keyword) { 13 | return other.value === this.value ? 0 : 1; 14 | } else { 15 | return -1; 16 | } 17 | } 18 | }; 19 | 20 | tree.True = new(tree.Keyword)('true'); 21 | tree.False = new(tree.Keyword)('false'); 22 | 23 | })(require('../tree')); 24 | -------------------------------------------------------------------------------- /lib/thirdparty/less/tree/negative.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Negative = function (node) { 4 | this.value = node; 5 | }; 6 | tree.Negative.prototype = { 7 | type: "Negative", 8 | accept: function (visitor) { 9 | this.value = visitor.visit(this.value); 10 | }, 11 | genCSS: function (env, output) { 12 | output.add('-'); 13 | this.value.genCSS(env, output); 14 | }, 15 | toCSS: tree.toCSS, 16 | eval: function (env) { 17 | if (env.isMathOn()) { 18 | return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env); 19 | } 20 | return new(tree.Negative)(this.value.eval(env)); 21 | } 22 | }; 23 | 24 | })(require('../tree')); 25 | -------------------------------------------------------------------------------- /lib/thirdparty/less/tree/operation.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Operation = function (op, operands, isSpaced) { 4 | this.op = op.trim(); 5 | this.operands = operands; 6 | this.isSpaced = isSpaced; 7 | }; 8 | tree.Operation.prototype = { 9 | type: "Operation", 10 | accept: function (visitor) { 11 | this.operands = visitor.visit(this.operands); 12 | }, 13 | eval: function (env) { 14 | var a = this.operands[0].eval(env), 15 | b = this.operands[1].eval(env); 16 | 17 | if (env.isMathOn()) { 18 | if (a instanceof tree.Dimension && b instanceof tree.Color) { 19 | a = a.toColor(); 20 | } 21 | if (b instanceof tree.Dimension && a instanceof tree.Color) { 22 | b = b.toColor(); 23 | } 24 | if (!a.operate) { 25 | throw { type: "Operation", 26 | message: "Operation on an invalid type" }; 27 | } 28 | 29 | return a.operate(env, this.op, b); 30 | } else { 31 | return new(tree.Operation)(this.op, [a, b], this.isSpaced); 32 | } 33 | }, 34 | genCSS: function (env, output) { 35 | this.operands[0].genCSS(env, output); 36 | if (this.isSpaced) { 37 | output.add(" "); 38 | } 39 | output.add(this.op); 40 | if (this.isSpaced) { 41 | output.add(" "); 42 | } 43 | this.operands[1].genCSS(env, output); 44 | }, 45 | toCSS: tree.toCSS 46 | }; 47 | 48 | tree.operate = function (env, op, a, b) { 49 | switch (op) { 50 | case '+': return a + b; 51 | case '-': return a - b; 52 | case '*': return a * b; 53 | case '/': return a / b; 54 | } 55 | }; 56 | 57 | })(require('../tree')); 58 | -------------------------------------------------------------------------------- /lib/thirdparty/less/tree/paren.js: -------------------------------------------------------------------------------- 1 | 2 | (function (tree) { 3 | 4 | tree.Paren = function (node) { 5 | this.value = node; 6 | }; 7 | tree.Paren.prototype = { 8 | type: "Paren", 9 | accept: function (visitor) { 10 | this.value = visitor.visit(this.value); 11 | }, 12 | genCSS: function (env, output) { 13 | output.add('('); 14 | this.value.genCSS(env, output); 15 | output.add(')'); 16 | }, 17 | toCSS: tree.toCSS, 18 | eval: function (env) { 19 | return new(tree.Paren)(this.value.eval(env)); 20 | } 21 | }; 22 | 23 | })(require('../tree')); 24 | -------------------------------------------------------------------------------- /lib/thirdparty/less/tree/quoted.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Quoted = function (str, content, escaped, index, currentFileInfo) { 4 | this.escaped = escaped; 5 | this.value = content || ''; 6 | this.quote = str.charAt(0); 7 | this.index = index; 8 | this.currentFileInfo = currentFileInfo; 9 | }; 10 | tree.Quoted.prototype = { 11 | type: "Quoted", 12 | genCSS: function (env, output) { 13 | if (!this.escaped) { 14 | output.add(this.quote, this.currentFileInfo, this.index); 15 | } 16 | output.add(this.value); 17 | if (!this.escaped) { 18 | output.add(this.quote); 19 | } 20 | }, 21 | toCSS: tree.toCSS, 22 | eval: function (env) { 23 | var that = this; 24 | var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { 25 | /* BEGIN MODIFICATION */ 26 | // Removed support for javascript 27 | const error = new Error("You are using JavaScript, which has been disabled."); 28 | error.index = that.index; 29 | error.type = "Syntax"; 30 | throw error; 31 | /* END MODIFICATION */ 32 | }).replace(/@\{([\w-]+)\}/g, function (_, name) { 33 | var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); 34 | return (v instanceof tree.Quoted) ? v.value : v.toCSS(); 35 | }); 36 | return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); 37 | }, 38 | compare: function (x) { 39 | if (!x.toCSS) { 40 | return -1; 41 | } 42 | 43 | var left = this.toCSS(), 44 | right = x.toCSS(); 45 | 46 | if (left === right) { 47 | return 0; 48 | } 49 | 50 | return left < right ? -1 : 1; 51 | } 52 | }; 53 | 54 | })(require('../tree')); 55 | -------------------------------------------------------------------------------- /lib/thirdparty/less/tree/rule.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Rule = function (name, value, important, merge, index, currentFileInfo, inline) { 4 | this.name = name; 5 | this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); 6 | this.important = important ? ' ' + important.trim() : ''; 7 | this.merge = merge; 8 | this.index = index; 9 | this.currentFileInfo = currentFileInfo; 10 | this.inline = inline || false; 11 | this.variable = name.charAt && (name.charAt(0) === '@'); 12 | }; 13 | 14 | tree.Rule.prototype = { 15 | type: "Rule", 16 | accept: function (visitor) { 17 | this.value = visitor.visit(this.value); 18 | }, 19 | genCSS: function (env, output) { 20 | output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index); 21 | try { 22 | this.value.genCSS(env, output); 23 | } 24 | catch(e) { 25 | e.index = this.index; 26 | e.filename = this.currentFileInfo.filename; 27 | throw e; 28 | } 29 | output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index); 30 | }, 31 | toCSS: tree.toCSS, 32 | eval: function (env) { 33 | var strictMathBypass = false, name = this.name; 34 | if (typeof name !== "string") { 35 | // expand 'primitive' name directly to get 36 | // things faster (~10% for benchmark.less): 37 | name = (name.length === 1) 38 | && (name[0] instanceof tree.Keyword) 39 | ? name[0].value : evalName(env, name); 40 | } 41 | if (name === "font" && !env.strictMath) { 42 | strictMathBypass = true; 43 | env.strictMath = true; 44 | } 45 | try { 46 | return new(tree.Rule)(name, 47 | this.value.eval(env), 48 | this.important, 49 | this.merge, 50 | this.index, this.currentFileInfo, this.inline); 51 | } 52 | catch(e) { 53 | e.index = e.index || this.index; 54 | throw e; 55 | } 56 | finally { 57 | if (strictMathBypass) { 58 | env.strictMath = false; 59 | } 60 | } 61 | }, 62 | makeImportant: function () { 63 | return new(tree.Rule)(this.name, 64 | this.value, 65 | "!important", 66 | this.merge, 67 | this.index, this.currentFileInfo, this.inline); 68 | } 69 | }; 70 | 71 | function evalName(env, name) { 72 | var value = "", i, n = name.length, 73 | output = {add: function (s) {value += s;}}; 74 | for (i = 0; i < n; i++) { 75 | name[i].eval(env).genCSS(env, output); 76 | } 77 | return value; 78 | } 79 | 80 | })(require('../tree')); 81 | -------------------------------------------------------------------------------- /lib/thirdparty/less/tree/unicode-descriptor.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.UnicodeDescriptor = function (value) { 4 | this.value = value; 5 | }; 6 | tree.UnicodeDescriptor.prototype = { 7 | type: "UnicodeDescriptor", 8 | genCSS: function (env, output) { 9 | output.add(this.value); 10 | }, 11 | toCSS: tree.toCSS, 12 | eval: function () { return this; } 13 | }; 14 | 15 | })(require('../tree')); 16 | -------------------------------------------------------------------------------- /lib/thirdparty/less/tree/url.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.URL = function (val, currentFileInfo, isEvald) { 4 | this.value = val; 5 | this.currentFileInfo = currentFileInfo; 6 | this.isEvald = isEvald; 7 | }; 8 | tree.URL.prototype = { 9 | type: "Url", 10 | accept: function (visitor) { 11 | this.value = visitor.visit(this.value); 12 | }, 13 | genCSS: function (env, output) { 14 | output.add("url("); 15 | this.value.genCSS(env, output); 16 | output.add(")"); 17 | }, 18 | toCSS: tree.toCSS, 19 | eval: function (ctx) { 20 | var val = this.value.eval(ctx), 21 | rootpath; 22 | 23 | if (!this.isEvald) { 24 | // Add the base path if the URL is relative 25 | rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; 26 | if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) { 27 | if (!val.quote) { 28 | rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); 29 | } 30 | val.value = rootpath + val.value; 31 | } 32 | 33 | val.value = ctx.normalizePath(val.value); 34 | 35 | // Add url args if enabled 36 | if (ctx.urlArgs) { 37 | if (!val.value.match(/^\s*data:/)) { 38 | var delimiter = val.value.indexOf('?') === -1 ? '?' : '&'; 39 | var urlArgs = delimiter + ctx.urlArgs; 40 | if (val.value.indexOf('#') !== -1) { 41 | val.value = val.value.replace('#', urlArgs + '#'); 42 | } else { 43 | val.value += urlArgs; 44 | } 45 | } 46 | } 47 | } 48 | 49 | return new(tree.URL)(val, this.currentFileInfo, true); 50 | } 51 | }; 52 | 53 | })(require('../tree')); 54 | -------------------------------------------------------------------------------- /lib/thirdparty/less/tree/value.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Value = function (value) { 4 | this.value = value; 5 | }; 6 | tree.Value.prototype = { 7 | type: "Value", 8 | accept: function (visitor) { 9 | if (this.value) { 10 | this.value = visitor.visitArray(this.value); 11 | } 12 | }, 13 | eval: function (env) { 14 | if (this.value.length === 1) { 15 | return this.value[0].eval(env); 16 | } else { 17 | return new(tree.Value)(this.value.map(function (v) { 18 | return v.eval(env); 19 | })); 20 | } 21 | }, 22 | genCSS: function (env, output) { 23 | var i; 24 | for(i = 0; i < this.value.length; i++) { 25 | this.value[i].genCSS(env, output); 26 | if (i+1 < this.value.length) { 27 | output.add((env && env.compress) ? ',' : ', '); 28 | } 29 | } 30 | }, 31 | toCSS: tree.toCSS 32 | }; 33 | 34 | })(require('../tree')); 35 | -------------------------------------------------------------------------------- /lib/thirdparty/less/tree/variable.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Variable = function (name, index, currentFileInfo) { 4 | this.name = name; 5 | this.index = index; 6 | this.currentFileInfo = currentFileInfo || {}; 7 | }; 8 | tree.Variable.prototype = { 9 | type: "Variable", 10 | eval: function (env) { 11 | var variable, name = this.name; 12 | 13 | if (name.indexOf('@@') === 0) { 14 | name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; 15 | } 16 | 17 | if (this.evaluating) { 18 | throw { type: 'Name', 19 | message: "Recursive variable definition for " + name, 20 | filename: this.currentFileInfo.file, 21 | index: this.index }; 22 | } 23 | 24 | this.evaluating = true; 25 | 26 | variable = tree.find(env.frames, function (frame) { 27 | var v = frame.variable(name); 28 | if (v) { 29 | return v.value.eval(env); 30 | } 31 | }); 32 | if (variable) { 33 | this.evaluating = false; 34 | return variable; 35 | } else { 36 | throw { type: 'Name', 37 | message: "variable " + name + " is undefined", 38 | filename: this.currentFileInfo.filename, 39 | index: this.index }; 40 | } 41 | } 42 | }; 43 | 44 | })(require('../tree')); 45 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "less-openui5", 3 | "version": "0.11.6", 4 | "description": "Build OpenUI5 themes with Less.js", 5 | "author": { 6 | "name": "SAP SE", 7 | "email": "openui5@sap.com", 8 | "url": "https://www.sap.com" 9 | }, 10 | "license": "Apache-2.0", 11 | "keywords": [ 12 | "openui5", 13 | "sapui5", 14 | "ui5", 15 | "less", 16 | "less.js", 17 | "theme" 18 | ], 19 | "main": "lib/index.js", 20 | "engines": { 21 | "node": "^20.11.0 || >=22.0.0", 22 | "npm": ">= 8" 23 | }, 24 | "scripts": { 25 | "lint": "eslint ./", 26 | "unit": "mocha test/*.js test/lib/**", 27 | "unit-debug": "mocha --inspect --inspect-brk test/*.js test/lib/**", 28 | "coverage": "nyc npm run unit", 29 | "test": "npm run lint && npm run coverage && npm run depcheck", 30 | "preversion": "npm test", 31 | "version": "git-chglog --next-tag v$npm_package_version -o CHANGELOG.md 0.7.0.. && git add CHANGELOG.md", 32 | "prepublishOnly": "git push --follow-tags", 33 | "release-note": "git-chglog -c .chglog/release-config.yml v$npm_package_version", 34 | "depcheck": "depcheck --ignores clean-css,source-map" 35 | }, 36 | "files": [ 37 | "CONTRIBUTING.md", 38 | "lib/**", 39 | "LICENSES/**", 40 | ".reuse/**" 41 | ], 42 | "nyc": { 43 | "reporter": [ 44 | "lcov", 45 | "text", 46 | "text-summary" 47 | ], 48 | "exclude": [ 49 | "coverage/**", 50 | "test/**", 51 | ".eslintrc.js", 52 | "lib/thirdparty/**" 53 | ], 54 | "check-coverage": true, 55 | "statements": 95, 56 | "branches": 85, 57 | "functions": 100, 58 | "lines": 95, 59 | "watermarks": { 60 | "statements": [ 61 | 70, 62 | 90 63 | ], 64 | "branches": [ 65 | 70, 66 | 90 67 | ], 68 | "functions": [ 69 | 70, 70 | 90 71 | ], 72 | "lines": [ 73 | 70, 74 | 90 75 | ] 76 | }, 77 | "cache": true, 78 | "all": true 79 | }, 80 | "repository": { 81 | "type": "git", 82 | "url": "git@github.com:SAP/less-openui5.git" 83 | }, 84 | "dependencies": { 85 | "@adobe/css-tools": "^4.4.3", 86 | "clone": "^2.1.2", 87 | "mime": "^1.6.0" 88 | }, 89 | "devDependencies": { 90 | "@eslint/js": "^9.15.0", 91 | "depcheck": "^1.4.7", 92 | "eslint": "^9.28.0", 93 | "eslint-config-google": "^0.14.0", 94 | "eslint-plugin-jsdoc": "^50.7.1", 95 | "globals": "^16.2.0", 96 | "graceful-fs": "^4.2.11", 97 | "mocha": "^11.5.0", 98 | "nyc": "^15.1.0", 99 | "sinon": "^16.1.3" 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /test/common/helper.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const fs = require("fs"); 4 | 5 | const crlfPattern = /\r\n/g; 6 | const lastLfPattern = /\n$/; 7 | const noLastLfPattern = /([^\n])$/; 8 | 9 | // file util 10 | function readFile(filename, lastLf) { 11 | const content = fs.readFileSync(filename, {encoding: "utf-8"}).replace(crlfPattern, "\n"); 12 | if (lastLf === false) { 13 | return content.replace(lastLfPattern, ""); // needed when using compress option 14 | } else { 15 | return content.replace(noLastLfPattern, "$1\n"); // adds last LF 16 | } 17 | } 18 | 19 | module.exports.readFile = readFile; 20 | -------------------------------------------------------------------------------- /test/expected/complex/test-cssvars-skeleton.css: -------------------------------------------------------------------------------- 1 | a, 2 | .link { 3 | color: var(--link-color); 4 | } 5 | a:hover { 6 | color: var(--link-color-hover); 7 | } 8 | .widget { 9 | color: #fff; 10 | background: var(--link-color); 11 | } 12 | .background { 13 | background-image: var(--backgroundImage); 14 | } 15 | .lazy-eval { 16 | width: var(--var); 17 | } 18 | .nested .calc-vars { 19 | height: var(--c); 20 | width: var(--d); 21 | color: var(--function_lighten14); 22 | top: calc(-1 * var(--top)); 23 | line-height: calc(var(--lineHeight) / 2); 24 | background: #428bca; 25 | border-color: #92bce0; 26 | background: #5697d0; 27 | } 28 | .nested .calc-vars-duplicate { 29 | color: var(--function_lighten14); 30 | padding-left: calc(20px + 0.5rem); 31 | border-color: #ffffff; 32 | background: #ffffff; 33 | } 34 | .nested .calc-vars-duplicate somePrefix.somePadding { 35 | padding: 1rem; 36 | box-sizing: border-box; 37 | } 38 | @media (max-width: 599px) { 39 | .nested .calc-vars-duplicate somePrefix.somePadding { 40 | padding: 0; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test/expected/complex/test-cssvars-variables.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --link-color: #428bca; 3 | --link-color-hover: #3071a9; 4 | --other-var: 0 0 0.25rem 0 rgba(0, 0, 0, 0.15), inset 0 -0.0625rem 0 0 #428bca; 5 | --link-color-active: var(--link-color); 6 | --backgroundVariant: 1; 7 | --backgroundImage: url(img.png); 8 | --a: 100%; 9 | --var: var(--a); 10 | --b: #245682; 11 | --padLeft: 20px; 12 | --top: 5rem; 13 | --lineHeight: 2rem; 14 | --c: calc(20%); 15 | --d: -100%; 16 | --function_lighten14: #3071a9; 17 | } 18 | -------------------------------------------------------------------------------- /test/expected/complex/test-cssvars-variables.source.less: -------------------------------------------------------------------------------- 1 | @link-color: #428bca; 2 | @link-color-hover: darken(@link-color, 10%); 3 | @other-var: 0 0 0.25rem 0 rgba(0, 0, 0, 0.15), inset 0 -0.0625rem 0 0 @link-color; 4 | @link-color-active: @link-color; 5 | @backgroundVariant: 1; 6 | @backgroundImage: url(img.png); 7 | @a: 100%; 8 | @var: @a; 9 | @b: darken(@link-color-active, 20%); 10 | @padLeft: 20px; 11 | @top: 5rem; 12 | @lineHeight: 2rem; 13 | @c: calc(100% - 80px); 14 | @d: -@a; 15 | @function_lighten14: lighten(@b, 10%); 16 | 17 | :root { 18 | --link-color: @link-color; 19 | --link-color-hover: @link-color-hover; 20 | --other-var: @other-var; 21 | --link-color-active: @link-color-active; 22 | --backgroundVariant: @backgroundVariant; 23 | --backgroundImage: @backgroundImage; 24 | --a: @a; 25 | --var: @var; 26 | --b: @b; 27 | --padLeft: @padLeft; 28 | --top: @top; 29 | --lineHeight: @lineHeight; 30 | --c: @c; 31 | --d: @d; 32 | --function_lighten14: @function_lighten14; 33 | } 34 | -------------------------------------------------------------------------------- /test/expected/complex/test.css: -------------------------------------------------------------------------------- 1 | a, 2 | .link { 3 | color: #428bca; 4 | } 5 | a:hover { 6 | color: #3071a9; 7 | } 8 | .widget { 9 | color: #fff; 10 | background: #428bca; 11 | } 12 | .background { 13 | background-image: url(img.png); 14 | } 15 | .lazy-eval { 16 | width: 9%; 17 | } 18 | .nested .calc-vars { 19 | height: calc(20%); 20 | width: -100%; 21 | color: #3071a9; 22 | top: -5rem; 23 | line-height: 1rem; 24 | background: #428bca; 25 | border-color: #92bce0; 26 | background: #5697d0; 27 | } 28 | .nested .calc-vars-duplicate { 29 | color: #3071a9; 30 | padding-left: calc(20px + 0.5rem); 31 | border-color: #ffffff; 32 | background: #ffffff; 33 | } 34 | .nested .calc-vars-duplicate somePrefix.somePadding { 35 | padding: 1rem; 36 | box-sizing: border-box; 37 | } 38 | @media (max-width: 599px) { 39 | .nested .calc-vars-duplicate somePrefix.somePadding { 40 | padding: 0; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test/expected/complex/test.min.css: -------------------------------------------------------------------------------- 1 | .myRule{float:left;color:#f00} 2 | -------------------------------------------------------------------------------- /test/expected/imports/main-no-relativeUrls.css: -------------------------------------------------------------------------------- 1 | @import "dir3/external.css"; 2 | /* dir3/inline.css */ 3 | 4 | /* dir1/foo.less */ 5 | .foo { 6 | background: url("foo.png"); 7 | } 8 | /* dir2/bar.less */ 9 | .bar { 10 | background: url("bar.png"); 11 | } -------------------------------------------------------------------------------- /test/expected/imports/main.css: -------------------------------------------------------------------------------- 1 | @import "dir3/external.css"; 2 | /* dir3/inline.css */ 3 | 4 | /* dir1/foo.less */ 5 | .foo { 6 | background: url("dir1/foo.png"); 7 | } 8 | /* dir2/bar.less */ 9 | .bar { 10 | background: url("dir2/bar.png"); 11 | } -------------------------------------------------------------------------------- /test/expected/libraries/empty/library-RTL.css: -------------------------------------------------------------------------------- 1 | 2 | /* Inline theming parameters */ 3 | #sap-ui-theme-my\.empty\.lib{background-image:url('data:text/plain;utf-8,%7B%7D')} 4 | -------------------------------------------------------------------------------- /test/expected/libraries/empty/library-parameters.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /test/expected/libraries/empty/library.css: -------------------------------------------------------------------------------- 1 | 2 | /* Inline theming parameters */ 3 | #sap-ui-theme-my\.empty\.lib{background-image:url('data:text/plain;utf-8,%7B%7D')} 4 | -------------------------------------------------------------------------------- /test/expected/libraries/lib1/my/ui/lib/themes/base/library-RTL.css: -------------------------------------------------------------------------------- 1 | .myUiLibRule1 { 2 | color: #fefefe; 3 | } 4 | .myUiLibRule2 { 5 | padding: 1px 4px 3px 2px; 6 | } 7 | 8 | /* Inline theming parameters */ 9 | #sap-ui-theme-my\.ui\.lib{background-image:url('data:text/plain;utf-8,%7B%22color1%22%3A%22%23fefefe%22%2C%22url1%22%3A%22url(%27111%27)%22%7D')} 10 | -------------------------------------------------------------------------------- /test/expected/libraries/lib1/my/ui/lib/themes/base/library.css: -------------------------------------------------------------------------------- 1 | .myUiLibRule1 { 2 | color: #fefefe; 3 | } 4 | .myUiLibRule2 { 5 | padding: 1px 2px 3px 4px; 6 | } 7 | 8 | /* Inline theming parameters */ 9 | #sap-ui-theme-my\.ui\.lib{background-image:url('data:text/plain;utf-8,%7B%22color1%22%3A%22%23fefefe%22%2C%22url1%22%3A%22url(%27111%27)%22%7D')} 10 | -------------------------------------------------------------------------------- /test/expected/libraries/lib1/my/ui/lib/themes/foo/css_variables.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --color1: #ffffff; 3 | --url1: url('../base/111'); 4 | } 5 | .fooContrast { 6 | --color1: #000000; 7 | } 8 | 9 | /* Inline theming parameters */ 10 | #sap-ui-theme-my\.ui\.lib{background-image:url('data:text/plain;utf-8,%7B%22default%22%3A%7B%22color1%22%3A%22%23ffffff%22%2C%22url1%22%3A%22url(%27..%2Fbase%2F111%27)%22%7D%2C%22scopes%22%3A%7B%22fooContrast%22%3A%7B%22color1%22%3A%22%23000000%22%7D%7D%7D')} 11 | -------------------------------------------------------------------------------- /test/expected/libraries/lib1/my/ui/lib/themes/foo/css_variables.source.less: -------------------------------------------------------------------------------- 1 | @color1: #ffffff; 2 | @url1: url('../base/111'); 3 | 4 | :root { 5 | --color1: @color1; 6 | --url1: @url1; 7 | } 8 | -------------------------------------------------------------------------------- /test/expected/libraries/lib1/my/ui/lib/themes/foo/library-RTL.css: -------------------------------------------------------------------------------- 1 | .myUiLibRule1 { 2 | color: #ffffff; 3 | } 4 | .myUiLibRule2 { 5 | padding: 1px 4px 3px 2px; 6 | } 7 | .fooContrast.myUiLibRule1, 8 | .fooContrast .myUiLibRule1 { 9 | color: #000000; 10 | } 11 | 12 | /* Inline theming parameters */ 13 | #sap-ui-theme-my\.ui\.lib{background-image:url('data:text/plain;utf-8,%7B%22default%22%3A%7B%22color1%22%3A%22%23ffffff%22%2C%22url1%22%3A%22url(%27..%2Fbase%2F111%27)%22%7D%2C%22scopes%22%3A%7B%22fooContrast%22%3A%7B%22color1%22%3A%22%23000000%22%7D%7D%7D')} 14 | -------------------------------------------------------------------------------- /test/expected/libraries/lib1/my/ui/lib/themes/foo/library.css: -------------------------------------------------------------------------------- 1 | .myUiLibRule1 { 2 | color: #ffffff; 3 | } 4 | .myUiLibRule2 { 5 | padding: 1px 2px 3px 4px; 6 | } 7 | .fooContrast.myUiLibRule1, 8 | .fooContrast .myUiLibRule1 { 9 | color: #000000; 10 | } 11 | 12 | /* Inline theming parameters */ 13 | #sap-ui-theme-my\.ui\.lib{background-image:url('data:text/plain;utf-8,%7B%22default%22%3A%7B%22color1%22%3A%22%23ffffff%22%2C%22url1%22%3A%22url(%27..%2Fbase%2F111%27)%22%7D%2C%22scopes%22%3A%7B%22fooContrast%22%3A%7B%22color1%22%3A%22%23000000%22%7D%7D%7D')} 14 | -------------------------------------------------------------------------------- /test/expected/libraries/lib1/my/ui/lib/themes/foo/library_skeleton-RTL.css: -------------------------------------------------------------------------------- 1 | .myUiLibRule1 { 2 | color: var(--color1); 3 | } 4 | .myUiLibRule2 { 5 | padding: 1px 4px 3px 2px; 6 | } 7 | .fooContrast.myUiLibRule1, 8 | .fooContrast .myUiLibRule1 { 9 | color: var(--color1); 10 | } 11 | -------------------------------------------------------------------------------- /test/expected/libraries/lib1/my/ui/lib/themes/foo/library_skeleton.css: -------------------------------------------------------------------------------- 1 | .myUiLibRule1 { 2 | color: var(--color1); 3 | } 4 | .myUiLibRule2 { 5 | padding: 1px 2px 3px 4px; 6 | } 7 | .fooContrast.myUiLibRule1, 8 | .fooContrast .myUiLibRule1 { 9 | color: var(--color1); 10 | } 11 | -------------------------------------------------------------------------------- /test/expected/libraries/lib2/my/ui/lib/themes/bar/library-RTL.css: -------------------------------------------------------------------------------- 1 | .myUiLibRule1 { 2 | color: #ffffff; 3 | } 4 | .myUiLibRule2 { 5 | padding: 1px 4px 3px 2px; 6 | } 7 | .barContrast.myUiLibRule1, 8 | .barContrast .myUiLibRule1 { 9 | color: #000000; 10 | } 11 | 12 | /* Inline theming parameters */ 13 | #sap-ui-theme-my\.ui\.lib{background-image:url('data:text/plain;utf-8,%7B%22default%22%3A%7B%22color1%22%3A%22%23ffffff%22%2C%22url1%22%3A%22url(%27..%2Fbase%2F111%27)%22%7D%2C%22scopes%22%3A%7B%22barContrast%22%3A%7B%22color1%22%3A%22%23000000%22%7D%7D%7D')} 14 | -------------------------------------------------------------------------------- /test/expected/libraries/lib2/my/ui/lib/themes/bar/library.css: -------------------------------------------------------------------------------- 1 | .myUiLibRule1 { 2 | color: #ffffff; 3 | } 4 | .myUiLibRule2 { 5 | padding: 1px 2px 3px 4px; 6 | } 7 | .barContrast.myUiLibRule1, 8 | .barContrast .myUiLibRule1 { 9 | color: #000000; 10 | } 11 | 12 | /* Inline theming parameters */ 13 | #sap-ui-theme-my\.ui\.lib{background-image:url('data:text/plain;utf-8,%7B%22default%22%3A%7B%22color1%22%3A%22%23ffffff%22%2C%22url1%22%3A%22url(%27..%2Fbase%2F111%27)%22%7D%2C%22scopes%22%3A%7B%22barContrast%22%3A%7B%22color1%22%3A%22%23000000%22%7D%7D%7D')} 14 | -------------------------------------------------------------------------------- /test/expected/libraries/lib3/my/other/ui/lib/themes/bar/library-RTL.css: -------------------------------------------------------------------------------- 1 | .myOtherUiLibRule1 { 2 | color: #ffffff; 3 | } 4 | .myOtherUiLibRule2 { 5 | padding: 1px 4px 3px 2px; 6 | } 7 | .barContrast.myOtherUiLibRule1, 8 | .barContrast .myOtherUiLibRule1 { 9 | color: #000000; 10 | } 11 | 12 | /* Inline theming parameters */ 13 | #sap-ui-theme-my\.other\.ui\.lib{background-image:url('data:text/plain;utf-8,%7B%22default%22%3A%7B%22_my_other_ui_lib_MyControl_color1%22%3A%22%23ffffff%22%2C%22_my_other_ui_lib_MyOtherControl_color1%22%3A%22%23ffffff%22%2C%22_my_other_ui_lib_MyControl_color2%22%3A%22%23008000%22%7D%2C%22scopes%22%3A%7B%22barContrast%22%3A%7B%22_my_other_ui_lib_MyControl_color1%22%3A%22%23000000%22%2C%22_my_other_ui_lib_MyOtherControl_color1%22%3A%22%23000000%22%7D%7D%7D')} 14 | -------------------------------------------------------------------------------- /test/expected/libraries/lib3/my/other/ui/lib/themes/bar/library.css: -------------------------------------------------------------------------------- 1 | .myOtherUiLibRule1 { 2 | color: #ffffff; 3 | } 4 | .myOtherUiLibRule2 { 5 | padding: 1px 2px 3px 4px; 6 | } 7 | .barContrast.myOtherUiLibRule1, 8 | .barContrast .myOtherUiLibRule1 { 9 | color: #000000; 10 | } 11 | 12 | /* Inline theming parameters */ 13 | #sap-ui-theme-my\.other\.ui\.lib{background-image:url('data:text/plain;utf-8,%7B%22default%22%3A%7B%22_my_other_ui_lib_MyControl_color1%22%3A%22%23ffffff%22%2C%22_my_other_ui_lib_MyOtherControl_color1%22%3A%22%23ffffff%22%2C%22_my_other_ui_lib_MyControl_color2%22%3A%22%23008000%22%7D%2C%22scopes%22%3A%7B%22barContrast%22%3A%7B%22_my_other_ui_lib_MyControl_color1%22%3A%22%23000000%22%2C%22_my_other_ui_lib_MyOtherControl_color1%22%3A%22%23000000%22%7D%7D%7D')} 14 | -------------------------------------------------------------------------------- /test/expected/libraries/lib3/my/other/ui/lib/themes/base/library-RTL.css: -------------------------------------------------------------------------------- 1 | .myOtherUiLibRule1 { 2 | color: #fefefe; 3 | } 4 | .myOtherUiLibRule2 { 5 | padding: 1px 4px 3px 2px; 6 | } 7 | 8 | /* Inline theming parameters */ 9 | #sap-ui-theme-my\.other\.ui\.lib{background-image:url('data:text/plain;utf-8,%7B%22_my_other_ui_lib_MyControl_color1%22%3A%22%23fefefe%22%2C%22_my_other_ui_lib_MyOtherControl_color1%22%3A%22%23fefefe%22%7D')} 10 | -------------------------------------------------------------------------------- /test/expected/libraries/lib3/my/other/ui/lib/themes/base/library.css: -------------------------------------------------------------------------------- 1 | .myOtherUiLibRule1 { 2 | color: #fefefe; 3 | } 4 | .myOtherUiLibRule2 { 5 | padding: 1px 2px 3px 4px; 6 | } 7 | 8 | /* Inline theming parameters */ 9 | #sap-ui-theme-my\.other\.ui\.lib{background-image:url('data:text/plain;utf-8,%7B%22_my_other_ui_lib_MyControl_color1%22%3A%22%23fefefe%22%2C%22_my_other_ui_lib_MyOtherControl_color1%22%3A%22%23fefefe%22%7D')} 10 | -------------------------------------------------------------------------------- /test/expected/libraries/lib3/my/other/ui/lib/themes/foo/library-RTL.css: -------------------------------------------------------------------------------- 1 | .myOtherUiLibRule1 { 2 | color: #ffffff; 3 | } 4 | .myOtherUiLibRule2 { 5 | padding: 1px 4px 3px 2px; 6 | } 7 | .fooContrast.myOtherUiLibRule1, 8 | .fooContrast .myOtherUiLibRule1 { 9 | color: #000000; 10 | } 11 | 12 | /* Inline theming parameters */ 13 | #sap-ui-theme-my\.other\.ui\.lib{background-image:url('data:text/plain;utf-8,%7B%22default%22%3A%7B%22_my_other_ui_lib_MyControl_color1%22%3A%22%23ffffff%22%2C%22_my_other_ui_lib_MyOtherControl_color1%22%3A%22%23ffffff%22%2C%22_my_other_ui_lib_MyControl_color2%22%3A%22%23008000%22%7D%2C%22scopes%22%3A%7B%22fooContrast%22%3A%7B%22_my_other_ui_lib_MyControl_color1%22%3A%22%23000000%22%2C%22_my_other_ui_lib_MyOtherControl_color1%22%3A%22%23000000%22%7D%7D%7D')} 14 | -------------------------------------------------------------------------------- /test/expected/libraries/lib3/my/other/ui/lib/themes/foo/library.css: -------------------------------------------------------------------------------- 1 | .myOtherUiLibRule1 { 2 | color: #ffffff; 3 | } 4 | .myOtherUiLibRule2 { 5 | padding: 1px 2px 3px 4px; 6 | } 7 | .fooContrast.myOtherUiLibRule1, 8 | .fooContrast .myOtherUiLibRule1 { 9 | color: #000000; 10 | } 11 | 12 | /* Inline theming parameters */ 13 | #sap-ui-theme-my\.other\.ui\.lib{background-image:url('data:text/plain;utf-8,%7B%22default%22%3A%7B%22_my_other_ui_lib_MyControl_color1%22%3A%22%23ffffff%22%2C%22_my_other_ui_lib_MyOtherControl_color1%22%3A%22%23ffffff%22%2C%22_my_other_ui_lib_MyControl_color2%22%3A%22%23008000%22%7D%2C%22scopes%22%3A%7B%22fooContrast%22%3A%7B%22_my_other_ui_lib_MyControl_color1%22%3A%22%23000000%22%2C%22_my_other_ui_lib_MyOtherControl_color1%22%3A%22%23000000%22%7D%7D%7D')} 14 | -------------------------------------------------------------------------------- /test/expected/libraries/lib4/my/ui/lib/themes/cascading/library-RTL.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: #000000; 3 | } 4 | -------------------------------------------------------------------------------- /test/expected/libraries/lib4/my/ui/lib/themes/cascading/library.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: #000000; 3 | } 4 | -------------------------------------------------------------------------------- /test/expected/libraries/lib5/my/ui/lib/themes/cascading/library-RTL.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: black; 3 | } 4 | a { 5 | color: black; 6 | } 7 | b { 8 | text-align: center; 9 | } 10 | .sapContrastPlus { 11 | color: orange; 12 | } 13 | 14 | body.sapContrastPlus, 15 | body .sapContrastPlus { 16 | color: blue; 17 | } 18 | 19 | .sapContrastPlus a { 20 | color: blue; 21 | } 22 | /* ============================= */ 23 | 24 | /* Shared CSS 1 theme */ 25 | 26 | /* ============================= */ 27 | 28 | /* ============================= */ 29 | 30 | /* Shared CSS 2 theme */ 31 | 32 | /* ============================= */ 33 | -------------------------------------------------------------------------------- /test/expected/libraries/lib5/my/ui/lib/themes/cascading/library.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: black; 3 | } 4 | a { 5 | color: black; 6 | } 7 | b { 8 | text-align: center; 9 | } 10 | .sapContrastPlus { 11 | color: orange; 12 | } 13 | 14 | body.sapContrastPlus, 15 | body .sapContrastPlus { 16 | color: blue; 17 | } 18 | 19 | .sapContrastPlus a { 20 | color: blue; 21 | } 22 | /* ============================= */ 23 | 24 | /* Shared CSS 1 theme */ 25 | 26 | /* ============================= */ 27 | 28 | /* ============================= */ 29 | 30 | /* Shared CSS 2 theme */ 31 | 32 | /* ============================= */ 33 | -------------------------------------------------------------------------------- /test/expected/libraries/scopes/comments/lib1/comments/themes/foo/library-RTL.css: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a sample comment! 3 | */ 4 | .myRule1 { 5 | /** 6 | * This is a sample comment inside a rule! 7 | */ 8 | color: #000000; 9 | } 10 | .fooContrast.myRule1, 11 | .fooContrast .myRule1 { 12 | color: #ffffff; 13 | } -------------------------------------------------------------------------------- /test/expected/libraries/scopes/comments/lib1/comments/themes/foo/library.css: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a sample comment! 3 | */ 4 | .myRule1 { 5 | /** 6 | * This is a sample comment inside a rule! 7 | */ 8 | color: #000000; 9 | } 10 | .fooContrast.myRule1, 11 | .fooContrast .myRule1 { 12 | color: #ffffff; 13 | } -------------------------------------------------------------------------------- /test/expected/libraries/scopes/comments/lib2/comments/themes/bar/library-RTL.css: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a sample comment! 3 | */ 4 | .myRule1 { 5 | /** 6 | * This is a sample comment inside a rule! 7 | */ 8 | color: #000000; 9 | } 10 | .barContrast.myRule1, 11 | .barContrast .myRule1 { 12 | color: #ffffff; 13 | } -------------------------------------------------------------------------------- /test/expected/libraries/scopes/comments/lib2/comments/themes/bar/library.css: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a sample comment! 3 | */ 4 | .myRule1 { 5 | /** 6 | * This is a sample comment inside a rule! 7 | */ 8 | color: #000000; 9 | } 10 | .barContrast.myRule1, 11 | .barContrast .myRule1 { 12 | color: #ffffff; 13 | } -------------------------------------------------------------------------------- /test/expected/libraries/scopes/comments/lib3/comments/themes/bar/library-RTL.css: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a sample comment! 3 | */ 4 | .myRule1 { 5 | /** 6 | * This is a sample comment inside a rule 7 | * with a different color value! 8 | */ 9 | color: #ffffff; 10 | } 11 | b { 12 | color: green; 13 | } 14 | a { 15 | color: green; 16 | } 17 | html { 18 | color: green; 19 | } 20 | -------------------------------------------------------------------------------- /test/expected/libraries/scopes/comments/lib3/comments/themes/bar/library.css: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a sample comment! 3 | */ 4 | .myRule1 { 5 | /** 6 | * This is a sample comment inside a rule 7 | * with a different color value! 8 | */ 9 | color: #ffffff; 10 | } 11 | b { 12 | color: green; 13 | } 14 | a { 15 | color: green; 16 | } 17 | html { 18 | color: green; 19 | } 20 | -------------------------------------------------------------------------------- /test/expected/libraries/scopes/css-scope-root/lib1/css-scope-root/themes/foo/library-RTL.css: -------------------------------------------------------------------------------- 1 | .myRule1 { 2 | color: #000000; 3 | } 4 | .myRule2 { 5 | color: #c3c3c3; 6 | } 7 | .fooContrast { 8 | color: #aaaaaa; 9 | } 10 | 11 | .fooContrast.myRule1, 12 | .fooContrast .myRule1 { 13 | color: #ffffff; 14 | } 15 | 16 | .fooContrast.myRule2, 17 | .fooContrast .myRule2 { 18 | color: #444444; 19 | } -------------------------------------------------------------------------------- /test/expected/libraries/scopes/css-scope-root/lib1/css-scope-root/themes/foo/library.css: -------------------------------------------------------------------------------- 1 | .myRule1 { 2 | color: #000000; 3 | } 4 | .myRule2 { 5 | color: #c3c3c3; 6 | } 7 | .fooContrast { 8 | color: #aaaaaa; 9 | } 10 | 11 | .fooContrast.myRule1, 12 | .fooContrast .myRule1 { 13 | color: #ffffff; 14 | } 15 | 16 | .fooContrast.myRule2, 17 | .fooContrast .myRule2 { 18 | color: #444444; 19 | } -------------------------------------------------------------------------------- /test/expected/libraries/scopes/css-scope-root/lib2/css-scope-root/themes/bar/library-RTL.css: -------------------------------------------------------------------------------- 1 | .myRule1 { 2 | color: #000000; 3 | } 4 | .myRule2 { 5 | color: #c3c3c3; 6 | } 7 | .barContrast { 8 | color: #aaaaaa; 9 | } 10 | 11 | .barContrast.myRule1, 12 | .barContrast .myRule1 { 13 | color: #ffffff; 14 | } 15 | 16 | .barContrast.myRule2, 17 | .barContrast .myRule2 { 18 | color: #444444; 19 | } -------------------------------------------------------------------------------- /test/expected/libraries/scopes/css-scope-root/lib2/css-scope-root/themes/bar/library.css: -------------------------------------------------------------------------------- 1 | .myRule1 { 2 | color: #000000; 3 | } 4 | .myRule2 { 5 | color: #c3c3c3; 6 | } 7 | .barContrast { 8 | color: #aaaaaa; 9 | } 10 | 11 | .barContrast.myRule1, 12 | .barContrast .myRule1 { 13 | color: #ffffff; 14 | } 15 | 16 | .barContrast.myRule2, 17 | .barContrast .myRule2 { 18 | color: #444444; 19 | } -------------------------------------------------------------------------------- /test/expected/libraries/scopes/default/lib1/default/themes/foo/library-RTL.css: -------------------------------------------------------------------------------- 1 | .myRule1 { 2 | color: #000000; 3 | } 4 | .myRule2 { 5 | color: #ffffff; 6 | float: right; 7 | } 8 | .myRule3 { 9 | display: -webkit-flex; 10 | display: flex; 11 | } 12 | .myRule4 { 13 | display: -webkit-box; 14 | display: -moz-box; 15 | display: -ms-flexbox; 16 | display: flex; 17 | } 18 | .myRule5, 19 | .myRule6, 20 | .myRule7 { 21 | color: #000000; 22 | } 23 | .myRule8 { 24 | display: block; 25 | } 26 | .sapContrast .sapMNLI { 27 | background: #ffffff; 28 | } 29 | .sapContrast .sapMNLG-GroupHeader { 30 | background: #000000; 31 | } 32 | .sapContrast .sapMNLG-Body .sapMNLI { 33 | background: #f2f2f2; 34 | } 35 | .fooContrast.myRule1, 36 | .fooContrast .myRule1 { 37 | color: #ffffff; 38 | } 39 | 40 | .fooContrast.myRule2, 41 | .fooContrast .myRule2 { 42 | color: #ffffff; 43 | } 44 | 45 | .fooContrast.myRule5, 46 | .fooContrast .myRule5, 47 | .fooContrast.myRule6, 48 | .fooContrast .myRule6, 49 | .fooContrast.myRule7, 50 | .fooContrast .myRule7 { 51 | color: #ffffff; 52 | } 53 | 54 | .sapContrast .sapMNLI { 55 | background: #000000; 56 | } 57 | 58 | .sapContrast .sapMNLG-GroupHeader { 59 | background: #000000; 60 | } 61 | 62 | .sapContrast .sapMNLG-Body .sapMNLI { 63 | background: #000000; 64 | } 65 | -------------------------------------------------------------------------------- /test/expected/libraries/scopes/default/lib1/default/themes/foo/library.css: -------------------------------------------------------------------------------- 1 | .myRule1 { 2 | color: #000000; 3 | } 4 | .myRule2 { 5 | color: #ffffff; 6 | float: left; 7 | } 8 | .myRule3 { 9 | display: -webkit-flex; 10 | display: flex; 11 | } 12 | .myRule4 { 13 | display: -webkit-box; 14 | display: -moz-box; 15 | display: -ms-flexbox; 16 | display: flex; 17 | } 18 | .myRule5, 19 | .myRule6, 20 | .myRule7 { 21 | color: #000000; 22 | } 23 | .myRule8 { 24 | display: block; 25 | } 26 | .sapContrast .sapMNLI { 27 | background: #ffffff; 28 | } 29 | .sapContrast .sapMNLG-GroupHeader { 30 | background: #000000; 31 | } 32 | .sapContrast .sapMNLG-Body .sapMNLI { 33 | background: #f2f2f2; 34 | } 35 | .fooContrast.myRule1, 36 | .fooContrast .myRule1 { 37 | color: #ffffff; 38 | } 39 | 40 | .fooContrast.myRule2, 41 | .fooContrast .myRule2 { 42 | color: #ffffff; 43 | } 44 | 45 | .fooContrast.myRule5, 46 | .fooContrast .myRule5, 47 | .fooContrast.myRule6, 48 | .fooContrast .myRule6, 49 | .fooContrast.myRule7, 50 | .fooContrast .myRule7 { 51 | color: #ffffff; 52 | } 53 | 54 | .sapContrast .sapMNLI { 55 | background: #000000; 56 | } 57 | 58 | .sapContrast .sapMNLG-GroupHeader { 59 | background: #000000; 60 | } 61 | 62 | .sapContrast .sapMNLG-Body .sapMNLI { 63 | background: #000000; 64 | } 65 | -------------------------------------------------------------------------------- /test/expected/libraries/scopes/default/lib2/default/themes/bar/library-RTL.css: -------------------------------------------------------------------------------- 1 | .myRule1 { 2 | color: #000000; 3 | } 4 | .myRule2 { 5 | color: #ffffff; 6 | float: right; 7 | } 8 | .myRule3 { 9 | display: -webkit-flex; 10 | display: flex; 11 | } 12 | .myRule4 { 13 | display: -webkit-box; 14 | display: -moz-box; 15 | display: -ms-flexbox; 16 | display: flex; 17 | } 18 | .myRule5, 19 | .myRule6, 20 | .myRule7 { 21 | color: #000000; 22 | } 23 | .myRule8 { 24 | display: block; 25 | } 26 | .sapContrast .sapMNLI { 27 | background: #ffffff; 28 | } 29 | .sapContrast .sapMNLG-GroupHeader { 30 | background: #000000; 31 | } 32 | .sapContrast .sapMNLG-Body .sapMNLI { 33 | background: #f2f2f2; 34 | } 35 | .barContrast.myRule1, 36 | .barContrast .myRule1 { 37 | color: #ffffff; 38 | } 39 | 40 | .barContrast.myRule2, 41 | .barContrast .myRule2 { 42 | color: #ffffff; 43 | } 44 | 45 | .barContrast.myRule5, 46 | .barContrast .myRule5, 47 | .barContrast.myRule6, 48 | .barContrast .myRule6, 49 | .barContrast.myRule7, 50 | .barContrast .myRule7 { 51 | color: #ffffff; 52 | } 53 | 54 | .sapContrast .sapMNLI { 55 | background: #000000; 56 | } 57 | 58 | .sapContrast .sapMNLG-GroupHeader { 59 | background: #000000; 60 | } 61 | 62 | .sapContrast .sapMNLG-Body .sapMNLI { 63 | background: #000000; 64 | } 65 | -------------------------------------------------------------------------------- /test/expected/libraries/scopes/default/lib2/default/themes/bar/library.css: -------------------------------------------------------------------------------- 1 | .myRule1 { 2 | color: #000000; 3 | } 4 | .myRule2 { 5 | color: #ffffff; 6 | float: left; 7 | } 8 | .myRule3 { 9 | display: -webkit-flex; 10 | display: flex; 11 | } 12 | .myRule4 { 13 | display: -webkit-box; 14 | display: -moz-box; 15 | display: -ms-flexbox; 16 | display: flex; 17 | } 18 | .myRule5, 19 | .myRule6, 20 | .myRule7 { 21 | color: #000000; 22 | } 23 | .myRule8 { 24 | display: block; 25 | } 26 | .sapContrast .sapMNLI { 27 | background: #ffffff; 28 | } 29 | .sapContrast .sapMNLG-GroupHeader { 30 | background: #000000; 31 | } 32 | .sapContrast .sapMNLG-Body .sapMNLI { 33 | background: #f2f2f2; 34 | } 35 | .barContrast.myRule1, 36 | .barContrast .myRule1 { 37 | color: #ffffff; 38 | } 39 | 40 | .barContrast.myRule2, 41 | .barContrast .myRule2 { 42 | color: #ffffff; 43 | } 44 | 45 | .barContrast.myRule5, 46 | .barContrast .myRule5, 47 | .barContrast.myRule6, 48 | .barContrast .myRule6, 49 | .barContrast.myRule7, 50 | .barContrast .myRule7 { 51 | color: #ffffff; 52 | } 53 | 54 | .sapContrast .sapMNLI { 55 | background: #000000; 56 | } 57 | 58 | .sapContrast .sapMNLG-GroupHeader { 59 | background: #000000; 60 | } 61 | 62 | .sapContrast .sapMNLG-Body .sapMNLI { 63 | background: #000000; 64 | } 65 | -------------------------------------------------------------------------------- /test/expected/libraries/scopes/dom/lib1/dom/themes/foo/library-RTL.css: -------------------------------------------------------------------------------- 1 | div.myRule1 { 2 | color: #000000; 3 | } 4 | div.myRule1 > .myRule2 { 5 | color: #000000; 6 | } 7 | .fooContrast div.myRule1 { 8 | color: #ffffff; 9 | } 10 | 11 | .fooContrast div.myRule1 > .myRule2 { 12 | color: #ffffff; 13 | } -------------------------------------------------------------------------------- /test/expected/libraries/scopes/dom/lib1/dom/themes/foo/library.css: -------------------------------------------------------------------------------- 1 | div.myRule1 { 2 | color: #000000; 3 | } 4 | div.myRule1 > .myRule2 { 5 | color: #000000; 6 | } 7 | .fooContrast div.myRule1 { 8 | color: #ffffff; 9 | } 10 | 11 | .fooContrast div.myRule1 > .myRule2 { 12 | color: #ffffff; 13 | } 14 | -------------------------------------------------------------------------------- /test/expected/libraries/scopes/dom/lib2/dom/themes/bar/library-RTL.css: -------------------------------------------------------------------------------- 1 | div.myRule1 { 2 | color: #000000; 3 | } 4 | div.myRule1 > .myRule2 { 5 | color: #000000; 6 | } 7 | .barContrast div.myRule1 { 8 | color: #ffffff; 9 | } 10 | 11 | .barContrast div.myRule1 > .myRule2 { 12 | color: #ffffff; 13 | } -------------------------------------------------------------------------------- /test/expected/libraries/scopes/dom/lib2/dom/themes/bar/library.css: -------------------------------------------------------------------------------- 1 | div.myRule1 { 2 | color: #000000; 3 | } 4 | div.myRule1 > .myRule2 { 5 | color: #000000; 6 | } 7 | .barContrast div.myRule1 { 8 | color: #ffffff; 9 | } 10 | 11 | .barContrast div.myRule1 > .myRule2 { 12 | color: #ffffff; 13 | } 14 | -------------------------------------------------------------------------------- /test/expected/libraries/scopes/empty-media-queries/lib1/empty-media-queries/themes/foo/library-RTL.css: -------------------------------------------------------------------------------- 1 | .myRule1 { 2 | color: #000000; 3 | } 4 | @media (min-width: 600px) and (max-width: 1023px) { 5 | .myRule1 { 6 | width: 100px; 7 | } 8 | } 9 | .fooContrast.myRule1, 10 | .fooContrast .myRule1 { 11 | color: #ffffff; 12 | } -------------------------------------------------------------------------------- /test/expected/libraries/scopes/empty-media-queries/lib1/empty-media-queries/themes/foo/library.css: -------------------------------------------------------------------------------- 1 | .myRule1 { 2 | color: #000000; 3 | } 4 | @media (min-width: 600px) and (max-width: 1023px) { 5 | .myRule1 { 6 | width: 100px; 7 | } 8 | } 9 | .fooContrast.myRule1, 10 | .fooContrast .myRule1 { 11 | color: #ffffff; 12 | } -------------------------------------------------------------------------------- /test/expected/libraries/scopes/empty-media-queries/lib2/empty-media-queries/themes/bar/library-RTL.css: -------------------------------------------------------------------------------- 1 | .myRule1 { 2 | color: #000000; 3 | } 4 | @media (min-width: 600px) and (max-width: 1023px) { 5 | .myRule1 { 6 | width: 100px; 7 | } 8 | } 9 | .barContrast.myRule1, 10 | .barContrast .myRule1 { 11 | color: #ffffff; 12 | } -------------------------------------------------------------------------------- /test/expected/libraries/scopes/empty-media-queries/lib2/empty-media-queries/themes/bar/library.css: -------------------------------------------------------------------------------- 1 | .myRule1 { 2 | color: #000000; 3 | } 4 | @media (min-width: 600px) and (max-width: 1023px) { 5 | .myRule1 { 6 | width: 100px; 7 | } 8 | } 9 | .barContrast.myRule1, 10 | .barContrast .myRule1 { 11 | color: #ffffff; 12 | } -------------------------------------------------------------------------------- /test/expected/libraries/scopes/html-compressed/lib1/html/themes/foo/library-RTL.css: -------------------------------------------------------------------------------- 1 | html[dir=rtl][data-sap-ui-browser^=ie] .sapUiGrid th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ie] .sapUiGrid td>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ed] .sapUiGrid th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ed] .sapUiGrid td>.sapUiFormTitle{color:#000000}html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid td>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid td>.sapUiFormTitle{color:#000000}html[dir=rtl][data-sap-ui-browser^=ie].sap-desktop-sapUiClass .testClass{color:#000000}html[dir=rtl][data-sap-ui-browser^=ie] .fooContrast .sapUiGrid th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ie] .fooContrast.sapUiGrid th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ie] .fooContrast .sapUiGrid td>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ie] .fooContrast.sapUiGrid td>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ed] .fooContrast .sapUiGrid th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ed] .fooContrast.sapUiGrid th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ed] .fooContrast .sapUiGrid td>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ed] .fooContrast.sapUiGrid td>.sapUiFormTitle{color:#ffffff;}html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid .fooContrast th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid .fooContrast td>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid .fooContrast th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid .fooContrast td>.sapUiFormTitle{color:#ffffff;}html[dir=rtl][data-sap-ui-browser^=ie].sap-desktop-sapUiClass .fooContrast .testClass,html[dir=rtl][data-sap-ui-browser^=ie].sap-desktop-sapUiClass .fooContrast.testClass{color:#ffffff;} -------------------------------------------------------------------------------- /test/expected/libraries/scopes/html-compressed/lib1/html/themes/foo/library.css: -------------------------------------------------------------------------------- 1 | html[dir=rtl][data-sap-ui-browser^=ie] .sapUiGrid th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ie] .sapUiGrid td>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ed] .sapUiGrid th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ed] .sapUiGrid td>.sapUiFormTitle{color:#000000}html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid td>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid td>.sapUiFormTitle{color:#000000}html[dir=rtl][data-sap-ui-browser^=ie].sap-desktop-sapUiClass .testClass{color:#000000}html[dir=rtl][data-sap-ui-browser^=ie] .fooContrast .sapUiGrid th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ie] .fooContrast.sapUiGrid th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ie] .fooContrast .sapUiGrid td>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ie] .fooContrast.sapUiGrid td>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ed] .fooContrast .sapUiGrid th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ed] .fooContrast.sapUiGrid th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ed] .fooContrast .sapUiGrid td>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ed] .fooContrast.sapUiGrid td>.sapUiFormTitle{color:#ffffff;}html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid .fooContrast th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid .fooContrast td>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid .fooContrast th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid .fooContrast td>.sapUiFormTitle{color:#ffffff;}html[dir=rtl][data-sap-ui-browser^=ie].sap-desktop-sapUiClass .fooContrast .testClass,html[dir=rtl][data-sap-ui-browser^=ie].sap-desktop-sapUiClass .fooContrast.testClass{color:#ffffff;} -------------------------------------------------------------------------------- /test/expected/libraries/scopes/html-compressed/lib2/html/themes/bar/library-RTL.css: -------------------------------------------------------------------------------- 1 | html[dir=rtl][data-sap-ui-browser^=ie] .sapUiGrid th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ie] .sapUiGrid td>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ed] .sapUiGrid th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ed] .sapUiGrid td>.sapUiFormTitle{color:#000000}html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid td>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid td>.sapUiFormTitle{color:#000000}html[dir=rtl][data-sap-ui-browser^=ie].sap-desktop-sapUiClass .testClass{color:#000000}html[dir=rtl][data-sap-ui-browser^=ie] .barContrast .sapUiGrid th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ie] .barContrast.sapUiGrid th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ie] .barContrast .sapUiGrid td>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ie] .barContrast.sapUiGrid td>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ed] .barContrast .sapUiGrid th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ed] .barContrast.sapUiGrid th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ed] .barContrast .sapUiGrid td>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ed] .barContrast.sapUiGrid td>.sapUiFormTitle{color:#ffffff;}html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid .barContrast th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid .barContrast td>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid .barContrast th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid .barContrast td>.sapUiFormTitle{color:#ffffff;}html[dir=rtl][data-sap-ui-browser^=ie].sap-desktop-sapUiClass .barContrast .testClass,html[dir=rtl][data-sap-ui-browser^=ie].sap-desktop-sapUiClass .barContrast.testClass{color:#ffffff;} -------------------------------------------------------------------------------- /test/expected/libraries/scopes/html-compressed/lib2/html/themes/bar/library.css: -------------------------------------------------------------------------------- 1 | html[dir=rtl][data-sap-ui-browser^=ie] .sapUiGrid th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ie] .sapUiGrid td>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ed] .sapUiGrid th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ed] .sapUiGrid td>.sapUiFormTitle{color:#000000}html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid td>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid td>.sapUiFormTitle{color:#000000}html[dir=rtl][data-sap-ui-browser^=ie].sap-desktop-sapUiClass .testClass{color:#000000}html[dir=rtl][data-sap-ui-browser^=ie] .barContrast .sapUiGrid th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ie] .barContrast.sapUiGrid th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ie] .barContrast .sapUiGrid td>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ie] .barContrast.sapUiGrid td>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ed] .barContrast .sapUiGrid th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ed] .barContrast.sapUiGrid th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ed] .barContrast .sapUiGrid td>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^=ed] .barContrast.sapUiGrid td>.sapUiFormTitle{color:#ffffff;}html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid .barContrast th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid .barContrast td>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid .barContrast th>.sapUiFormTitle,html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid .barContrast td>.sapUiFormTitle{color:#ffffff;}html[dir=rtl][data-sap-ui-browser^=ie].sap-desktop-sapUiClass .barContrast .testClass,html[dir=rtl][data-sap-ui-browser^=ie].sap-desktop-sapUiClass .barContrast.testClass{color:#ffffff;} -------------------------------------------------------------------------------- /test/expected/libraries/scopes/html/lib1/html/themes/foo/library-RTL.css: -------------------------------------------------------------------------------- 1 | html[dir=rtl][data-sap-ui-browser^=ie] .sapUiGrid th > .sapUiFormTitle, 2 | html[dir=rtl][data-sap-ui-browser^=ie] .sapUiGrid td > .sapUiFormTitle, 3 | html[dir=rtl][data-sap-ui-browser^=ed] .sapUiGrid th > .sapUiFormTitle, 4 | html[dir=rtl][data-sap-ui-browser^=ed] .sapUiGrid td > .sapUiFormTitle { 5 | color: #000000; 6 | } 7 | html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid th > .sapUiFormTitle, 8 | html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid td > .sapUiFormTitle, 9 | html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid th > .sapUiFormTitle, 10 | html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid td > .sapUiFormTitle { 11 | color: #000000; 12 | } 13 | html[dir=rtl][data-sap-ui-browser^=ie].sap-desktop-sapUiClass .testClass { 14 | color: #000000; 15 | } 16 | html[dir=rtl][data-sap-ui-browser^=ie] .fooContrast .sapUiGrid th > .sapUiFormTitle, 17 | html[dir=rtl][data-sap-ui-browser^=ie] .fooContrast.sapUiGrid th > .sapUiFormTitle, 18 | html[dir=rtl][data-sap-ui-browser^=ie] .fooContrast .sapUiGrid td > .sapUiFormTitle, 19 | html[dir=rtl][data-sap-ui-browser^=ie] .fooContrast.sapUiGrid td > .sapUiFormTitle, 20 | html[dir=rtl][data-sap-ui-browser^=ed] .fooContrast .sapUiGrid th > .sapUiFormTitle, 21 | html[dir=rtl][data-sap-ui-browser^=ed] .fooContrast.sapUiGrid th > .sapUiFormTitle, 22 | html[dir=rtl][data-sap-ui-browser^=ed] .fooContrast .sapUiGrid td > .sapUiFormTitle, 23 | html[dir=rtl][data-sap-ui-browser^=ed] .fooContrast.sapUiGrid td > .sapUiFormTitle { 24 | color: #ffffff; 25 | } 26 | 27 | html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid .fooContrast th > .sapUiFormTitle, 28 | html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid .fooContrast td > .sapUiFormTitle, 29 | html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid .fooContrast th > .sapUiFormTitle, 30 | html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid .fooContrast td > .sapUiFormTitle { 31 | color: #ffffff; 32 | } 33 | 34 | html[dir=rtl][data-sap-ui-browser^=ie].sap-desktop-sapUiClass .fooContrast .testClass, 35 | html[dir=rtl][data-sap-ui-browser^=ie].sap-desktop-sapUiClass .fooContrast.testClass { 36 | color: #ffffff; 37 | } -------------------------------------------------------------------------------- /test/expected/libraries/scopes/html/lib1/html/themes/foo/library.css: -------------------------------------------------------------------------------- 1 | html[dir=rtl][data-sap-ui-browser^=ie] .sapUiGrid th > .sapUiFormTitle, 2 | html[dir=rtl][data-sap-ui-browser^=ie] .sapUiGrid td > .sapUiFormTitle, 3 | html[dir=rtl][data-sap-ui-browser^=ed] .sapUiGrid th > .sapUiFormTitle, 4 | html[dir=rtl][data-sap-ui-browser^=ed] .sapUiGrid td > .sapUiFormTitle { 5 | color: #000000; 6 | } 7 | html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid th > .sapUiFormTitle, 8 | html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid td > .sapUiFormTitle, 9 | html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid th > .sapUiFormTitle, 10 | html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid td > .sapUiFormTitle { 11 | color: #000000; 12 | } 13 | html[dir=rtl][data-sap-ui-browser^=ie].sap-desktop-sapUiClass .testClass { 14 | color: #000000; 15 | } 16 | html[dir=rtl][data-sap-ui-browser^=ie] .fooContrast .sapUiGrid th > .sapUiFormTitle, 17 | html[dir=rtl][data-sap-ui-browser^=ie] .fooContrast.sapUiGrid th > .sapUiFormTitle, 18 | html[dir=rtl][data-sap-ui-browser^=ie] .fooContrast .sapUiGrid td > .sapUiFormTitle, 19 | html[dir=rtl][data-sap-ui-browser^=ie] .fooContrast.sapUiGrid td > .sapUiFormTitle, 20 | html[dir=rtl][data-sap-ui-browser^=ed] .fooContrast .sapUiGrid th > .sapUiFormTitle, 21 | html[dir=rtl][data-sap-ui-browser^=ed] .fooContrast.sapUiGrid th > .sapUiFormTitle, 22 | html[dir=rtl][data-sap-ui-browser^=ed] .fooContrast .sapUiGrid td > .sapUiFormTitle, 23 | html[dir=rtl][data-sap-ui-browser^=ed] .fooContrast.sapUiGrid td > .sapUiFormTitle { 24 | color: #ffffff; 25 | } 26 | 27 | html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid .fooContrast th > .sapUiFormTitle, 28 | html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid .fooContrast td > .sapUiFormTitle, 29 | html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid .fooContrast th > .sapUiFormTitle, 30 | html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid .fooContrast td > .sapUiFormTitle { 31 | color: #ffffff; 32 | } 33 | 34 | html[dir=rtl][data-sap-ui-browser^=ie].sap-desktop-sapUiClass .fooContrast .testClass, 35 | html[dir=rtl][data-sap-ui-browser^=ie].sap-desktop-sapUiClass .fooContrast.testClass { 36 | color: #ffffff; 37 | } -------------------------------------------------------------------------------- /test/expected/libraries/scopes/html/lib2/html/themes/bar/library-RTL.css: -------------------------------------------------------------------------------- 1 | html[dir=rtl][data-sap-ui-browser^=ie] .sapUiGrid th > .sapUiFormTitle, 2 | html[dir=rtl][data-sap-ui-browser^=ie] .sapUiGrid td > .sapUiFormTitle, 3 | html[dir=rtl][data-sap-ui-browser^=ed] .sapUiGrid th > .sapUiFormTitle, 4 | html[dir=rtl][data-sap-ui-browser^=ed] .sapUiGrid td > .sapUiFormTitle { 5 | color: #000000; 6 | } 7 | html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid th > .sapUiFormTitle, 8 | html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid td > .sapUiFormTitle, 9 | html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid th > .sapUiFormTitle, 10 | html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid td > .sapUiFormTitle { 11 | color: #000000; 12 | } 13 | html[dir=rtl][data-sap-ui-browser^=ie].sap-desktop-sapUiClass .testClass { 14 | color: #000000; 15 | } 16 | html[dir=rtl][data-sap-ui-browser^=ie] .barContrast .sapUiGrid th > .sapUiFormTitle, 17 | html[dir=rtl][data-sap-ui-browser^=ie] .barContrast.sapUiGrid th > .sapUiFormTitle, 18 | html[dir=rtl][data-sap-ui-browser^=ie] .barContrast .sapUiGrid td > .sapUiFormTitle, 19 | html[dir=rtl][data-sap-ui-browser^=ie] .barContrast.sapUiGrid td > .sapUiFormTitle, 20 | html[dir=rtl][data-sap-ui-browser^=ed] .barContrast .sapUiGrid th > .sapUiFormTitle, 21 | html[dir=rtl][data-sap-ui-browser^=ed] .barContrast.sapUiGrid th > .sapUiFormTitle, 22 | html[dir=rtl][data-sap-ui-browser^=ed] .barContrast .sapUiGrid td > .sapUiFormTitle, 23 | html[dir=rtl][data-sap-ui-browser^=ed] .barContrast.sapUiGrid td > .sapUiFormTitle { 24 | color: #ffffff; 25 | } 26 | 27 | html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid .barContrast th > .sapUiFormTitle, 28 | html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid .barContrast td > .sapUiFormTitle, 29 | html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid .barContrast th > .sapUiFormTitle, 30 | html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid .barContrast td > .sapUiFormTitle { 31 | color: #ffffff; 32 | } 33 | 34 | html[dir=rtl][data-sap-ui-browser^=ie].sap-desktop-sapUiClass .barContrast .testClass, 35 | html[dir=rtl][data-sap-ui-browser^=ie].sap-desktop-sapUiClass .barContrast.testClass { 36 | color: #ffffff; 37 | } -------------------------------------------------------------------------------- /test/expected/libraries/scopes/html/lib2/html/themes/bar/library.css: -------------------------------------------------------------------------------- 1 | html[dir=rtl][data-sap-ui-browser^=ie] .sapUiGrid th > .sapUiFormTitle, 2 | html[dir=rtl][data-sap-ui-browser^=ie] .sapUiGrid td > .sapUiFormTitle, 3 | html[dir=rtl][data-sap-ui-browser^=ed] .sapUiGrid th > .sapUiFormTitle, 4 | html[dir=rtl][data-sap-ui-browser^=ed] .sapUiGrid td > .sapUiFormTitle { 5 | color: #000000; 6 | } 7 | html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid th > .sapUiFormTitle, 8 | html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid td > .sapUiFormTitle, 9 | html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid th > .sapUiFormTitle, 10 | html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid td > .sapUiFormTitle { 11 | color: #000000; 12 | } 13 | html[dir=rtl][data-sap-ui-browser^=ie].sap-desktop-sapUiClass .testClass { 14 | color: #000000; 15 | } 16 | html[dir=rtl][data-sap-ui-browser^=ie] .barContrast .sapUiGrid th > .sapUiFormTitle, 17 | html[dir=rtl][data-sap-ui-browser^=ie] .barContrast.sapUiGrid th > .sapUiFormTitle, 18 | html[dir=rtl][data-sap-ui-browser^=ie] .barContrast .sapUiGrid td > .sapUiFormTitle, 19 | html[dir=rtl][data-sap-ui-browser^=ie] .barContrast.sapUiGrid td > .sapUiFormTitle, 20 | html[dir=rtl][data-sap-ui-browser^=ed] .barContrast .sapUiGrid th > .sapUiFormTitle, 21 | html[dir=rtl][data-sap-ui-browser^=ed] .barContrast.sapUiGrid th > .sapUiFormTitle, 22 | html[dir=rtl][data-sap-ui-browser^=ed] .barContrast .sapUiGrid td > .sapUiFormTitle, 23 | html[dir=rtl][data-sap-ui-browser^=ed] .barContrast.sapUiGrid td > .sapUiFormTitle { 24 | color: #ffffff; 25 | } 26 | 27 | html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid .barContrast th > .sapUiFormTitle, 28 | html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid .barContrast td > .sapUiFormTitle, 29 | html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid .barContrast th > .sapUiFormTitle, 30 | html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid .barContrast td > .sapUiFormTitle { 31 | color: #ffffff; 32 | } 33 | 34 | html[dir=rtl][data-sap-ui-browser^=ie].sap-desktop-sapUiClass .barContrast .testClass, 35 | html[dir=rtl][data-sap-ui-browser^=ie].sap-desktop-sapUiClass .barContrast.testClass { 36 | color: #ffffff; 37 | } -------------------------------------------------------------------------------- /test/expected/libraries/scopes/media-queries/lib1/media-queries/themes/foo/library-RTL.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'myfont'; 3 | src: url('font.eot'); 4 | font-weight: normal; 5 | font-style: normal; 6 | font-stretch: normal; 7 | } 8 | @media (min-width: 600px) and (max-width: 1023px) { 9 | .myRule1 { 10 | color: #000000; 11 | } 12 | .myRule2 { 13 | color: #ffffff; 14 | } 15 | .myRule3 { 16 | display: -webkit-flex; 17 | display: flex; 18 | } 19 | .sapMALI { 20 | display: -webkit-box; 21 | display: -moz-box; 22 | display: -ms-flexbox; 23 | display: flex; 24 | } 25 | .sapMALI .sapMLIBUnread, 26 | .sapMALI .sapMLIBSelectS, 27 | .sapMALI .sapMLIBSelectM, 28 | .sapMALI .sapMLIBSelectD { 29 | display: none; 30 | } 31 | .sapMALI { 32 | height: 3rem; 33 | } 34 | } 35 | @media (min-width: 600px) and (max-width: 1023px) { 36 | .fooContrast.myRule1, 37 | .fooContrast .myRule1 { 38 | color: #ffffff; 39 | } 40 | 41 | .fooContrast.myRule2, 42 | .fooContrast .myRule2 { 43 | color: #ffffff; 44 | } 45 | } -------------------------------------------------------------------------------- /test/expected/libraries/scopes/media-queries/lib1/media-queries/themes/foo/library.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'myfont'; 3 | src: url('font.eot'); 4 | font-weight: normal; 5 | font-style: normal; 6 | font-stretch: normal; 7 | } 8 | @media (min-width: 600px) and (max-width: 1023px) { 9 | .myRule1 { 10 | color: #000000; 11 | } 12 | .myRule2 { 13 | color: #ffffff; 14 | } 15 | .myRule3 { 16 | display: -webkit-flex; 17 | display: flex; 18 | } 19 | .sapMALI { 20 | display: -webkit-box; 21 | display: -moz-box; 22 | display: -ms-flexbox; 23 | display: flex; 24 | } 25 | .sapMALI .sapMLIBUnread, 26 | .sapMALI .sapMLIBSelectS, 27 | .sapMALI .sapMLIBSelectM, 28 | .sapMALI .sapMLIBSelectD { 29 | display: none; 30 | } 31 | .sapMALI { 32 | height: 3rem; 33 | } 34 | } 35 | @media (min-width: 600px) and (max-width: 1023px) { 36 | .fooContrast.myRule1, 37 | .fooContrast .myRule1 { 38 | color: #ffffff; 39 | } 40 | 41 | .fooContrast.myRule2, 42 | .fooContrast .myRule2 { 43 | color: #ffffff; 44 | } 45 | } -------------------------------------------------------------------------------- /test/expected/libraries/scopes/media-queries/lib2/media-queries/themes/bar/library-RTL.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'myfont'; 3 | src: url('font.eot'); 4 | font-weight: normal; 5 | font-style: normal; 6 | font-stretch: normal; 7 | } 8 | @media (min-width: 600px) and (max-width: 1023px) { 9 | .myRule1 { 10 | color: #000000; 11 | } 12 | .myRule2 { 13 | color: #ffffff; 14 | } 15 | .myRule3 { 16 | display: -webkit-flex; 17 | display: flex; 18 | } 19 | .sapMALI { 20 | display: -webkit-box; 21 | display: -moz-box; 22 | display: -ms-flexbox; 23 | display: flex; 24 | } 25 | .sapMALI .sapMLIBUnread, 26 | .sapMALI .sapMLIBSelectS, 27 | .sapMALI .sapMLIBSelectM, 28 | .sapMALI .sapMLIBSelectD { 29 | display: none; 30 | } 31 | .sapMALI { 32 | height: 3rem; 33 | } 34 | } 35 | @media (min-width: 600px) and (max-width: 1023px) { 36 | .barContrast.myRule1, 37 | .barContrast .myRule1 { 38 | color: #ffffff; 39 | } 40 | 41 | .barContrast.myRule2, 42 | .barContrast .myRule2 { 43 | color: #ffffff; 44 | } 45 | } -------------------------------------------------------------------------------- /test/expected/libraries/scopes/media-queries/lib2/media-queries/themes/bar/library.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'myfont'; 3 | src: url('font.eot'); 4 | font-weight: normal; 5 | font-style: normal; 6 | font-stretch: normal; 7 | } 8 | @media (min-width: 600px) and (max-width: 1023px) { 9 | .myRule1 { 10 | color: #000000; 11 | } 12 | .myRule2 { 13 | color: #ffffff; 14 | } 15 | .myRule3 { 16 | display: -webkit-flex; 17 | display: flex; 18 | } 19 | .sapMALI { 20 | display: -webkit-box; 21 | display: -moz-box; 22 | display: -ms-flexbox; 23 | display: flex; 24 | } 25 | .sapMALI .sapMLIBUnread, 26 | .sapMALI .sapMLIBSelectS, 27 | .sapMALI .sapMLIBSelectM, 28 | .sapMALI .sapMLIBSelectD { 29 | display: none; 30 | } 31 | .sapMALI { 32 | height: 3rem; 33 | } 34 | } 35 | @media (min-width: 600px) and (max-width: 1023px) { 36 | .barContrast.myRule1, 37 | .barContrast .myRule1 { 38 | color: #ffffff; 39 | } 40 | 41 | .barContrast.myRule2, 42 | .barContrast .myRule2 { 43 | color: #ffffff; 44 | } 45 | } -------------------------------------------------------------------------------- /test/expected/libraries/scopes/mixins/lib1/mixins/themes/foo/library-RTL.css: -------------------------------------------------------------------------------- 1 | @media screen and (min-width: 600px) { 2 | body { 3 | background: #000000; 4 | font-size: 21px; 5 | } 6 | html.myRule1 td { 7 | color: #000000; 8 | } 9 | } 10 | @media screen and (min-width: 600px) { 11 | body.fooContrast, 12 | body .fooContrast { 13 | background: #ffffff; 14 | font-size: 20px; 15 | } 16 | 17 | html.myRule1 .fooContrast td { 18 | color: #ffffff; 19 | } 20 | } -------------------------------------------------------------------------------- /test/expected/libraries/scopes/mixins/lib1/mixins/themes/foo/library.css: -------------------------------------------------------------------------------- 1 | @media screen and (min-width: 600px) { 2 | body { 3 | background: #000000; 4 | font-size: 21px; 5 | } 6 | html.myRule1 td { 7 | color: #000000; 8 | } 9 | } 10 | @media screen and (min-width: 600px) { 11 | body.fooContrast, 12 | body .fooContrast { 13 | background: #ffffff; 14 | font-size: 20px; 15 | } 16 | 17 | html.myRule1 .fooContrast td { 18 | color: #ffffff; 19 | } 20 | } -------------------------------------------------------------------------------- /test/expected/libraries/scopes/mixins/lib2/mixins/themes/bar/library-RTL.css: -------------------------------------------------------------------------------- 1 | @media screen and (min-width: 600px) { 2 | body { 3 | background: #000000; 4 | font-size: 21px; 5 | } 6 | html.myRule1 td { 7 | color: #000000; 8 | } 9 | } 10 | @media screen and (min-width: 600px) { 11 | body.barContrast, 12 | body .barContrast { 13 | background: #ffffff; 14 | font-size: 20px; 15 | } 16 | 17 | html.myRule1 .barContrast td { 18 | color: #ffffff; 19 | } 20 | } 21 | .myRule2 { 22 | background: #ffffff; 23 | } 24 | 25 | @media screen and (min-width: 600px) { 26 | body { 27 | background: #ffffff; 28 | font-size: 20px; 29 | } 30 | 31 | .myRule2 { 32 | background: #ffffff; 33 | } 34 | } -------------------------------------------------------------------------------- /test/expected/libraries/scopes/mixins/lib2/mixins/themes/bar/library.css: -------------------------------------------------------------------------------- 1 | @media screen and (min-width: 600px) { 2 | body { 3 | background: #000000; 4 | font-size: 21px; 5 | } 6 | html.myRule1 td { 7 | color: #000000; 8 | } 9 | } 10 | @media screen and (min-width: 600px) { 11 | body.barContrast, 12 | body .barContrast { 13 | background: #ffffff; 14 | font-size: 20px; 15 | } 16 | 17 | html.myRule1 .barContrast td { 18 | color: #ffffff; 19 | } 20 | } 21 | .myRule2 { 22 | background: #ffffff; 23 | } 24 | 25 | @media screen and (min-width: 600px) { 26 | body { 27 | background: #ffffff; 28 | font-size: 20px; 29 | } 30 | 31 | .myRule2 { 32 | background: #ffffff; 33 | } 34 | } -------------------------------------------------------------------------------- /test/expected/libraries/scopes/multiple-imports/lib1/multiple-imports/themes/foo/library-RTL.css: -------------------------------------------------------------------------------- 1 | .myRule1 { 2 | color: #ffffff; 3 | } 4 | .myRule2 { 5 | display: block; 6 | } 7 | .myRule5 { 8 | color: #ffffff; 9 | } 10 | .myRule6 { 11 | display: block; 12 | } 13 | .fooContrast.myRule1, 14 | .fooContrast .myRule1 { 15 | color: #ffffff; 16 | } 17 | 18 | .fooContrast.myRule5, 19 | .fooContrast .myRule5 { 20 | color: #ffffff; 21 | } -------------------------------------------------------------------------------- /test/expected/libraries/scopes/multiple-imports/lib1/multiple-imports/themes/foo/library.css: -------------------------------------------------------------------------------- 1 | .myRule1 { 2 | color: #ffffff; 3 | } 4 | .myRule2 { 5 | display: block; 6 | } 7 | .myRule5 { 8 | color: #ffffff; 9 | } 10 | .myRule6 { 11 | display: block; 12 | } 13 | .fooContrast.myRule1, 14 | .fooContrast .myRule1 { 15 | color: #ffffff; 16 | } 17 | 18 | .fooContrast.myRule5, 19 | .fooContrast .myRule5 { 20 | color: #ffffff; 21 | } -------------------------------------------------------------------------------- /test/expected/libraries/scopes/multiple-imports/lib2/multiple-imports/themes/bar/library-RTL.css: -------------------------------------------------------------------------------- 1 | .myRule1 { 2 | color: #ffffff; 3 | } 4 | .myRule2 { 5 | display: block; 6 | } 7 | .myRule5 { 8 | color: #ffffff; 9 | } 10 | .myRule6 { 11 | display: block; 12 | } 13 | .barContrast.myRule1, 14 | .barContrast .myRule1 { 15 | color: #ffffff; 16 | } 17 | 18 | .barContrast.myRule5, 19 | .barContrast .myRule5 { 20 | color: #ffffff; 21 | } 22 | .myRule3 { 23 | color: red; 24 | } 25 | 26 | .myRule4 { 27 | background-color: transparent !important; 28 | } 29 | 30 | .myRule7 { 31 | color: red; 32 | } 33 | -------------------------------------------------------------------------------- /test/expected/libraries/scopes/multiple-imports/lib2/multiple-imports/themes/bar/library.css: -------------------------------------------------------------------------------- 1 | .myRule1 { 2 | color: #ffffff; 3 | } 4 | .myRule2 { 5 | display: block; 6 | } 7 | .myRule5 { 8 | color: #ffffff; 9 | } 10 | .myRule6 { 11 | display: block; 12 | } 13 | .barContrast.myRule1, 14 | .barContrast .myRule1 { 15 | color: #ffffff; 16 | } 17 | 18 | .barContrast.myRule5, 19 | .barContrast .myRule5 { 20 | color: #ffffff; 21 | } 22 | .myRule3 { 23 | color: red; 24 | } 25 | 26 | .myRule4 { 27 | background-color: transparent !important; 28 | } 29 | 30 | .myRule7 { 31 | color: red; 32 | } -------------------------------------------------------------------------------- /test/expected/rtl/background-position.css: -------------------------------------------------------------------------------- 1 | /* RTL background-position mirroring */ 2 | .rtlBackgroundPositionMirroring { 3 | background-position: top; 4 | background-position: left; 5 | background-position: right top; 6 | background-position: 2cm 1cm; 7 | background-position: 80% 40%; 8 | background-position: 70%; 9 | background-position: 99.5%; 10 | background-position: 120% 40%; 11 | background-position: 100% 0; 12 | background-position: 0 0; 13 | background-position: 0% 0; 14 | background-position: 0 100%; 15 | background-position: 0% /* some comment */ 100%; 16 | background-position: top, top; 17 | background-position: right top, right top, center bottom; 18 | background-position: 2cm 1cm,3cm 4cm; 19 | background-position: 80% 40%,90% 90%,67% 66%; 20 | background-position: /* some comment */ 70%, 40%; 21 | background-position: 20em 40%, 10px 90%,67% 66%; 22 | background-position: 130%; 23 | background-position: 120% /* some comment */ -40%, /* some comment */ 105% 30%, 110% 80%; 24 | } 25 | .rtlBackgroundPositionMirroring1 { 26 | background-position: top; 27 | background-position: left; 28 | background-position: right top; 29 | background-position: 2cm 1cm; 30 | background-position: 80% 40%; 31 | background-position: 70%; 32 | background-position: 99.5%; 33 | background-position: 120% 40%; 34 | background-position: 100% 0; 35 | background-position: 0 0; 36 | background-position: 0% 0; 37 | background-position: 0 100%; 38 | background-position: 0% /* some comment */ 100%; 39 | background-position: top, top; 40 | background-position: right top, right top, center bottom; 41 | background-position: 2cm 1cm,3cm 4cm; 42 | background-position: 80% 40%,90% 90%,67% 66%; 43 | background-position: /* some comment */ 70%, 40%; 44 | background-position: 20em 40%, 10px 90%,67% 66%; 45 | background-position: 130%; 46 | background-position: 120% /* some comment */ -40%, /* some comment */ 105% 30%, 110% 80%; 47 | } 48 | .rtlBackgroundPositionMirroring2 { 49 | background-position: top; 50 | background-position: left; 51 | background-position: right top; 52 | background-position: 2cm 1cm; 53 | background-position: 80% 40%; 54 | background-position: 70%; 55 | background-position: 99.5%; 56 | background-position: 120% 40%; 57 | background-position: 100% 0; 58 | background-position: 0 0; 59 | background-position: 0% 0; 60 | background-position: 0 100%; 61 | background-position: 0% /* some comment */ 100%; 62 | background-position: top, top; 63 | background-position: right top, right top, center bottom; 64 | background-position: 2cm 1cm,3cm 4cm; 65 | background-position: 80% 40%,90% 90%,67% 66%; 66 | background-position: /* some comment */ 70%, 40%; 67 | background-position: 20em 40%, 10px 90%,67% 66%; 68 | background-position: 130%; 69 | background-position: 120% /* some comment */ -40%, /* some comment */ 105% 30%, 110% 80%; 70 | } 71 | -------------------------------------------------------------------------------- /test/expected/rtl/cursor.css: -------------------------------------------------------------------------------- 1 | /* RTL cursor mirroring */ 2 | .rtlCursorMirroring-auto { 3 | cursor: auto; 4 | } 5 | .rtlCursorMirroring-default { 6 | cursor: default; 7 | } 8 | .rtlCursorMirroring-pointer { 9 | cursor: pointer; 10 | } 11 | .rtlCursorMirroring-e-resize { 12 | cursor: /* some comment */ w-resize; 13 | } 14 | .rtlCursorMirroring-w-resize { 15 | cursor: e-resize; 16 | } 17 | .rtlCursorMirroring-ne-resize { 18 | cursor: nw-resize /* some comment */; 19 | } 20 | .rtlCursorMirroring-nw-resize { 21 | cursor: ne-resize; 22 | } 23 | .rtlCursorMirroring-se-resize { 24 | cursor: sw-resize; 25 | } 26 | .rtlCursorMirroring-sw-resize { 27 | cursor: se-resize; 28 | } 29 | .rtlCursorMirroring-nesw-resize { 30 | cursor: nwse-resize; 31 | } 32 | .rtlCursorMirroring-nwse-resize { 33 | cursor: nesw-resize; 34 | } 35 | .rtlCursorMirroring-url-auto { 36 | cursor: url(something), auto; 37 | } 38 | .rtlCursorMirroring-url-e-resize { 39 | cursor: /* some comment */ url(something), w-resize; 40 | } 41 | .rtlCursorMirroring-url-url-w-resize { 42 | cursor: url(something), /* some comment */ url(something2), e-resize; 43 | } 44 | .rtlCursorMirroring-auto1 { 45 | cursor: auto; 46 | } 47 | .rtlCursorMirroring-default1 { 48 | cursor: default; 49 | } 50 | .rtlCursorMirroring-pointer1 { 51 | cursor: pointer; 52 | } 53 | .rtlCursorMirroring-e-resize1 { 54 | cursor: /* some comment */ w-resize; 55 | } 56 | .rtlCursorMirroring-w-resize1 { 57 | cursor: e-resize; 58 | } 59 | .rtlCursorMirroring-ne-resize1 { 60 | cursor: nw-resize /* some comment */; 61 | } 62 | .rtlCursorMirroring-nw-resize1 { 63 | cursor: ne-resize; 64 | } 65 | .rtlCursorMirroring-se-resize1 { 66 | cursor: sw-resize; 67 | } 68 | .rtlCursorMirroring-sw-resize1 { 69 | cursor: se-resize; 70 | } 71 | .rtlCursorMirroring-nesw-resize1 { 72 | cursor: nwse-resize; 73 | } 74 | .rtlCursorMirroring-nwse-resize1 { 75 | cursor: nesw-resize; 76 | } 77 | .rtlCursorMirroring-url-auto1 { 78 | cursor: url(something), auto; 79 | } 80 | .rtlCursorMirroring-url-e-resize1 { 81 | cursor: /* some comment */ url(something), w-resize; 82 | } 83 | .rtlCursorMirroring-url-url-w-resize1 { 84 | cursor: url(something), /* some comment */ url(something2), e-resize; 85 | } 86 | .rtlCursorMirroring-auto2 { 87 | cursor: auto; 88 | } 89 | .rtlCursorMirroring-default2 { 90 | cursor: default; 91 | } 92 | .rtlCursorMirroring-pointer2 { 93 | cursor: pointer; 94 | } 95 | .rtlCursorMirroring-e-resize2 { 96 | cursor: /* some comment */ w-resize; 97 | } 98 | .rtlCursorMirroring-w-resize2 { 99 | cursor: e-resize; 100 | } 101 | .rtlCursorMirroring-ne-resize2 { 102 | cursor: nw-resize /* some comment */; 103 | } 104 | .rtlCursorMirroring-nw-resize2 { 105 | cursor: ne-resize; 106 | } 107 | .rtlCursorMirroring-se-resize2 { 108 | cursor: sw-resize; 109 | } 110 | .rtlCursorMirroring-sw-resize2 { 111 | cursor: se-resize; 112 | } 113 | .rtlCursorMirroring-nesw-resize2 { 114 | cursor: nwse-resize; 115 | } 116 | .rtlCursorMirroring-nwse-resize2 { 117 | cursor: nesw-resize; 118 | } 119 | .rtlCursorMirroring-url-auto2 { 120 | cursor: url(something), auto; 121 | } 122 | .rtlCursorMirroring-url-e-resize2 { 123 | cursor: /* some comment */ url(something), w-resize; 124 | } 125 | .rtlCursorMirroring-url-url-w-resize2 { 126 | cursor: url(something), /* some comment */ url(something2), e-resize; 127 | } 128 | -------------------------------------------------------------------------------- /test/expected/rtl/image-url.css: -------------------------------------------------------------------------------- 1 | /* RTL image url mirroring */ 2 | .rtlImageUrlMirroringRelative { 3 | background: url(img-RTL/fg/hi.png) /* some comment */; 4 | background-image: /* some comment */ url(img-RTL/fg/hi.png); 5 | list-style-image: url(img-RTL/fg/hi.png); 6 | } 7 | .rtlImageUrlMirroringAbsolute { 8 | background: url(http://abc.de/img/fg/hi.png) /* some comment */; 9 | } 10 | .rtlImageUrlMirroringQuotes { 11 | background: url('http://abc.de/img/fg/hi.png'); 12 | background: /* some comment */ url("http://abc.de/img/fg/hi.png"); 13 | background: url('img-RTL/column_header.gif') repeat /* some comment */ scroll 0 0 #d2dae8; 14 | background: url("img-RTL/column_header.gif") repeat scroll 0 0 #d2dae8 /* some comment */; 15 | } 16 | .rtlImageUrlMirroringMultiple { 17 | background: url(http://abc.de/img/fg/hi.png), /* some comment */ url(http://abc.de/img/fg/hi.png); 18 | } 19 | .rtlImageUrlMirroringRelative1 { 20 | background: url(img-RTL/fg/hi.png) /* some comment */; 21 | background-image: /* some comment */ url(img-RTL/fg/hi.png); 22 | list-style-image: url(img-RTL/fg/hi.png); 23 | } 24 | .rtlImageUrlMirroringAbsolute1 { 25 | background: url(http://abc.de/img/fg/hi.png) /* some comment */; 26 | } 27 | .rtlImageUrlMirroringQuotes1 { 28 | background: url('http://abc.de/img/fg/hi.png'); 29 | background: /* some comment */ url("http://abc.de/img/fg/hi.png"); 30 | background: url('img-RTL/column_header.gif') repeat /* some comment */ scroll 0 0 #d2dae8; 31 | background: url("img-RTL/column_header.gif") repeat scroll 0 0 #d2dae8 /* some comment */; 32 | } 33 | .rtlImageUrlMirroringMultiple1 { 34 | background: url(http://abc.de/img/fg/hi.png), /* some comment */ url(http://abc.de/img/fg/hi.png); 35 | } 36 | .rtlImageUrlMirroringRelative2 { 37 | background: url(img-RTL/fg/hi.png) /* some comment */; 38 | background-image: /* some comment */ url(img-RTL/fg/hi.png); 39 | list-style-image: url(img-RTL/fg/hi.png); 40 | } 41 | .rtlImageUrlMirroringAbsolute2 { 42 | background: url(http://abc.de/img/fg/hi.png) /* some comment */; 43 | } 44 | .rtlImageUrlMirroringQuotes2 { 45 | background: url('http://abc.de/img/fg/hi.png'); 46 | background: /* some comment */ url("http://abc.de/img/fg/hi.png"); 47 | background: url('img-RTL/column_header.gif') repeat /* some comment */ scroll 0 0 #d2dae8; 48 | background: url("img-RTL/column_header.gif") repeat scroll 0 0 #d2dae8 /* some comment */; 49 | } 50 | .rtlImageUrlMirroringMultiple2 { 51 | background: url(http://abc.de/img/fg/hi.png), /* some comment */ url(http://abc.de/img/fg/hi.png); 52 | } 53 | -------------------------------------------------------------------------------- /test/expected/rtl/shadow.css: -------------------------------------------------------------------------------- 1 | /* RTL box-shadow mirroring */ 2 | .rtlBoxShadowMirroring { 3 | box-shadow: none; 4 | box-shadow: inherit; 5 | box-shadow: -1px 2px; 6 | box-shadow: 5cm 2px; 7 | box-shadow: -1px 2px red; 8 | box-shadow: -1px 2px #010203; 9 | box-shadow: -2px -20px 30px 2px rgba(0, 0, 0, 0.15); 10 | text-shadow: 1em -0.0625rem 0 rgba(0, 0, 0, 0.6); 11 | text-shadow: 0 -0.0625rem 0 rgba(0, 0, 0, 0.6); 12 | box-shadow: /* some comment */ -1px 2px, -3px 4px; 13 | box-shadow: -1px 2px, -3px 4px, 5px 6px; 14 | box-shadow: -1px 2px 10px, -3px 4px 20px /* some comment */; 15 | box-shadow: -1px -2cm 10px 2ex, 3px 4em 20px #ff0000; 16 | box-shadow: -1px -2cm 10px 2ex #00ffac, 3px 4em 20px #0c212a; 17 | box-shadow: /* some comment */ inset -1px 2px; 18 | box-shadow: inset 5cm 2px; 19 | box-shadow: inset -1px 2px red; 20 | box-shadow: inset -1px 2px #010203; 21 | box-shadow: inset /* some comment */ -1px 2px, -3px 4px; 22 | box-shadow: -1px 2px, inset -3px 4px, 5px 6px; 23 | box-shadow: inset -1px -2cm 10px 2ex #00ffac, inset 3px 4em 20px #0c212a; 24 | -moz-box-shadow: -1px 2px; 25 | -webkit-box-shadow: -1px 2px; 26 | box-shadow: -5px 5px 10px rgba(0, 0, 0, 0.5), inset -2px 3px; 27 | -moz-box-shadow: -5px 5px 10px rgba(0, 0, 0, 0.5), inset -2px 3px; 28 | -webkit-box-shadow: -5px 5px 10px rgba(0, 0, 0, 0.5), inset -2px 3px; 29 | } 30 | .rtlBoxShadowMirroring1 { 31 | box-shadow: inset -1px 1px 0px 0px #000000, inset 1px -1px 0px 0px #123456, inset 0 -0.1875rem 0 #333333, inset 0 -0.25rem #000000; 32 | background: url("chess.png") right top / 10% #808080 round fixed border-box; 33 | left: 0; 34 | } 35 | .rtlBoxShadowMirroring2 { 36 | box-shadow: inset -1px 1px 0px 0px #000000, inset 1px -1px 0px 0px #123456, inset 0 -0.1875rem 0 #333333, inset 0 -0.25rem #000000; 37 | background: url("chess.png") right top / 10% #808080 round fixed border-box; 38 | left: 0; 39 | } 40 | /* RTL text-shadow mirroring */ 41 | .rtlTextShadowMirroring { 42 | text-shadow: none; 43 | text-shadow: inherit; 44 | text-shadow: -1px /* some comment */ 2px; 45 | text-shadow: 5cm 2px; 46 | text-shadow: -1px 2px /* some comment */ #ff0000; 47 | text-shadow: -1px 2px #010203; 48 | text-shadow: -1px 2px,-3px 4px; 49 | text-shadow: -1px 2px, -3px /* some comment */ 4px, 5px 6px /* some comment */; 50 | } 51 | -------------------------------------------------------------------------------- /test/expected/rtl/variables.css: -------------------------------------------------------------------------------- 1 | /* Variables */ 2 | .variables { 3 | color: #123456; 4 | background-color: #111111; 5 | } 6 | -------------------------------------------------------------------------------- /test/expected/simple/test-RTL.css: -------------------------------------------------------------------------------- 1 | .myRule { 2 | float: right; 3 | color: #ff0000; 4 | } 5 | @keyframes myKeyframes { 6 | 0%, 7 | 80%, 8 | 100% { 9 | transform: scale(1); 10 | } 11 | 40% { 12 | transform: scale(2); 13 | } 14 | } 15 | @font-face { 16 | font-family: "MyFont"; 17 | src: url("MyFont.woff2") format("woff2"); 18 | } 19 | -------------------------------------------------------------------------------- /test/expected/simple/test-RTL.min.css: -------------------------------------------------------------------------------- 1 | .myRule{float:right;color:#f00}@keyframes myKeyframes{0%,80%,100%{transform:scale(1)}40%{transform:scale(2)}}@font-face{font-family:"MyFont";src:url("MyFont.woff2") format("woff2")} 2 | -------------------------------------------------------------------------------- /test/expected/simple/test-cssvars-skeleton.css: -------------------------------------------------------------------------------- 1 | .myRule { 2 | float: left; 3 | color: var(--myColor); 4 | } 5 | @keyframes myKeyframes { 6 | 0%, 7 | 80%, 8 | 100% { 9 | transform: var(--myScale1); 10 | } 11 | 40% { 12 | transform: var(--myScale2); 13 | } 14 | } 15 | @font-face { 16 | font-family: "MyFont"; 17 | src: url("MyFont.woff2") format("woff2"); 18 | } 19 | -------------------------------------------------------------------------------- /test/expected/simple/test-cssvars-variables.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --myColor: #ff0000; 3 | --myFontFamily: "MyFont"; 4 | --myFontUrl: url("MyFont.woff2"); 5 | --myScale1: scale(1); 6 | --myScale2: scale(2); 7 | } 8 | -------------------------------------------------------------------------------- /test/expected/simple/test-cssvars-variables.source.less: -------------------------------------------------------------------------------- 1 | @myColor: #ff0000; 2 | @myFontFamily: "MyFont"; 3 | @myFontUrl: url("MyFont.woff2"); 4 | @myScale1: scale(1); 5 | @myScale2: scale(2); 6 | 7 | :root { 8 | --myColor: @myColor; 9 | --myFontFamily: @myFontFamily; 10 | --myFontUrl: @myFontUrl; 11 | --myScale1: @myScale1; 12 | --myScale2: @myScale2; 13 | } 14 | -------------------------------------------------------------------------------- /test/expected/simple/test-inline-parameters.css: -------------------------------------------------------------------------------- 1 | .myRule { 2 | float: left; 3 | color: #ff0000; 4 | } 5 | @keyframes myKeyframes { 6 | 0%, 7 | 80%, 8 | 100% { 9 | transform: scale(1); 10 | } 11 | 40% { 12 | transform: scale(2); 13 | } 14 | } 15 | @font-face { 16 | font-family: "MyFont"; 17 | src: url("MyFont.woff2") format("woff2"); 18 | } 19 | 20 | /* Inline theming parameters */ 21 | #sap-ui-theme-foo\.bar{background-image:url('data:text/plain;utf-8,%7B%22myColor%22%3A%22%23ff0000%22%2C%22myFontFamily%22%3A%22%5C%22MyFont%5C%22%22%2C%22myFontUrl%22%3A%22url(%5C%22MyFont.woff2%5C%22)%22%2C%22myScale1%22%3A%22scale(1)%22%2C%22myScale2%22%3A%22scale(2)%22%7D')} 22 | -------------------------------------------------------------------------------- /test/expected/simple/test-variables.json: -------------------------------------------------------------------------------- 1 | { 2 | "myColor": "#ff0000", 3 | "myFontFamily": "\"MyFont\"", 4 | "myFontUrl": "url(\"MyFont.woff2\")", 5 | "myScale1": "scale(1)", 6 | "myScale2": "scale(2)" 7 | } 8 | -------------------------------------------------------------------------------- /test/expected/simple/test-variables.min.json: -------------------------------------------------------------------------------- 1 | { "myColor" : "#f00", "myFontFamily": "\"MyFont\"", "myFontUrl": "url(\"MyFont.woff2\")", "myScale1": "scale(1)", "myScale2": "scale(2)" } 2 | -------------------------------------------------------------------------------- /test/expected/simple/test.css: -------------------------------------------------------------------------------- 1 | .myRule { 2 | float: left; 3 | color: #ff0000; 4 | } 5 | @keyframes myKeyframes { 6 | 0%, 7 | 80%, 8 | 100% { 9 | transform: scale(1); 10 | } 11 | 40% { 12 | transform: scale(2); 13 | } 14 | } 15 | @font-face { 16 | font-family: "MyFont"; 17 | src: url("MyFont.woff2") format("woff2"); 18 | } 19 | -------------------------------------------------------------------------------- /test/expected/simple/test.min.css: -------------------------------------------------------------------------------- 1 | .myRule{float:left;color:#f00}@keyframes myKeyframes{0%,80%,100%{transform:scale(1)}40%{transform:scale(2)}}@font-face{font-family:"MyFont";src:url("MyFont.woff2") format("woff2")} 2 | -------------------------------------------------------------------------------- /test/expected/variables/main.css: -------------------------------------------------------------------------------- 1 | .myRule { 2 | color: #808080; 3 | background-color: #cccccc; 4 | } 5 | .myRule2 { 6 | background-color: #ffffff; 7 | color: #808080; 8 | } -------------------------------------------------------------------------------- /test/expected/variables/variables.json: -------------------------------------------------------------------------------- 1 | { 2 | "myVar": "#808080", 3 | "myVar2": "#808080", 4 | "myOtherPublicVar": "'test'" 5 | } 6 | -------------------------------------------------------------------------------- /test/fixtures/complex/test.less: -------------------------------------------------------------------------------- 1 | // Variables 2 | @link-color: #428bca; // sea blue 3 | @link-color-hover: darken(@link-color, 10%); 4 | @other-var: 0 0 0.25rem 0 rgba(0, 0, 0, 0.15), inset 0 -0.0625rem 0 0 @link-color; 5 | @link-color-active: @link-color; 6 | @backgroundVariant: 2; 7 | @backgroundImage: extract(url(./img.png) none, @backgroundVariant); 8 | @backgroundVariant: 1; 9 | 10 | // Usage 11 | a, 12 | .link { 13 | color: @link-color; 14 | } 15 | a:hover { 16 | color: @link-color-hover; 17 | } 18 | .widget { 19 | color: #fff; 20 | background: @link-color; 21 | } 22 | .background { 23 | background-image: @backgroundImage; 24 | } 25 | 26 | // Lazy Evaluation 27 | .lazy-eval { 28 | width: @var; 29 | @a: 9%; 30 | } 31 | 32 | @var: @a; 33 | @a: 100%; 34 | 35 | .nested { 36 | 37 | @b: darken(@link-color-active, 20%); 38 | @padLeft: 20px; 39 | @top: 5rem; 40 | @lineHeight: 2rem; 41 | 42 | .my-mixin(@bgColor: #fff) { 43 | background: @bgColor; 44 | border-color: lighten(@bgColor, 20%); 45 | & when not (@bgColor = #000) { 46 | background: lighten(@bgColor, 5%); 47 | } 48 | } 49 | 50 | 51 | .other-mixin(@rootClass) { 52 | @{rootClass}.somePadding { 53 | padding: 1rem; 54 | box-sizing: border-box; 55 | } 56 | 57 | @media (max-width:599px) { 58 | @{rootClass}.somePadding { 59 | padding: 0; 60 | } 61 | } 62 | } 63 | 64 | .calc-vars { 65 | @c: calc(100% - 80px); 66 | @d: -@a; 67 | height: @c; 68 | width: @d; 69 | color: lighten(@b, 10%); 70 | top: -@top; 71 | line-height: @lineHeight / 2; 72 | .my-mixin(@link-color) 73 | } 74 | 75 | .calc-vars-duplicate { 76 | color: lighten(@b, 10%); 77 | padding-left: ~"calc(@{padLeft} + 0.5rem)"; 78 | .my-mixin(#fff); 79 | .other-mixin(somePrefix); 80 | } 81 | 82 | } 83 | 84 | -------------------------------------------------------------------------------- /test/fixtures/diff/css/library1/base.css: -------------------------------------------------------------------------------- 1 | /* 2 | * multiline 3 | */ 4 | a { 5 | color: blue; 6 | } 7 | /* one */ 8 | /* two */ 9 | /* three */ 10 | b { 11 | color: green; 12 | } 13 | /* single */ 14 | b.test { 15 | color: yellow; 16 | } -------------------------------------------------------------------------------- /test/fixtures/diff/css/library1/compare.css: -------------------------------------------------------------------------------- 1 | /* 2 | * multiline 3 | */ 4 | a { 5 | color: black; 6 | } 7 | /* one */ 8 | /* two */ 9 | /* three */ 10 | b { 11 | color: black; 12 | } 13 | /* mine */ 14 | html { 15 | background-color: black; 16 | } 17 | /* mine2 */ 18 | /* single */ 19 | b.test { 20 | color: black; 21 | } -------------------------------------------------------------------------------- /test/fixtures/diff/css/library2/base.css: -------------------------------------------------------------------------------- 1 | a { 2 | color: blue; 3 | } 4 | /* one */ 5 | b { 6 | color: green; 7 | } 8 | b.test { 9 | color: yellow; 10 | } 11 | /* two */ 12 | -------------------------------------------------------------------------------- /test/fixtures/diff/css/library2/compare.css: -------------------------------------------------------------------------------- 1 | a { 2 | color: blue; 3 | } 4 | /* one */ 5 | b { 6 | color: green; 7 | } 8 | -------------------------------------------------------------------------------- /test/fixtures/error/main.less: -------------------------------------------------------------------------------- 1 | @import "undefined-var.less"; 2 | -------------------------------------------------------------------------------- /test/fixtures/error/undefined-var.less: -------------------------------------------------------------------------------- 1 | .foo { 2 | color: @bar; 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/imports/dir1/foo.less: -------------------------------------------------------------------------------- 1 | /* dir1/foo.less */ 2 | 3 | .foo { 4 | background: url("foo.png"); 5 | } 6 | -------------------------------------------------------------------------------- /test/fixtures/imports/dir2/bar.less: -------------------------------------------------------------------------------- 1 | /* dir2/bar.less */ 2 | 3 | .bar { 4 | background: url("bar.png"); 5 | } 6 | -------------------------------------------------------------------------------- /test/fixtures/imports/dir3/external.css: -------------------------------------------------------------------------------- 1 | /* dir3/external.css */ 2 | -------------------------------------------------------------------------------- /test/fixtures/imports/dir3/inline.css: -------------------------------------------------------------------------------- 1 | /* dir3/inline.css */ 2 | -------------------------------------------------------------------------------- /test/fixtures/imports/main.less: -------------------------------------------------------------------------------- 1 | @import "dir1/foo.less"; 2 | @import "dir2/bar.less"; 3 | @import "dir3/external.css"; 4 | @import (inline) "dir3/inline.css"; 5 | -------------------------------------------------------------------------------- /test/fixtures/libraries/empty/empty.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP/less-openui5/382f8fba664b7c6d298e0762d5167cf31e4836ef/test/fixtures/libraries/empty/empty.less -------------------------------------------------------------------------------- /test/fixtures/libraries/lib1/my/ui/lib/themes/base/global.less: -------------------------------------------------------------------------------- 1 | @color1: #fefefe; 2 | @url1: url('111'); 3 | -------------------------------------------------------------------------------- /test/fixtures/libraries/lib1/my/ui/lib/themes/base/library.source.less: -------------------------------------------------------------------------------- 1 | @import "global.less"; 2 | 3 | .myUiLibRule1 { 4 | color: @color1; 5 | } 6 | 7 | .myUiLibRule2 { 8 | padding: 1px 2px 3px 4px; 9 | } 10 | -------------------------------------------------------------------------------- /test/fixtures/libraries/lib1/my/ui/lib/themes/foo/global.less: -------------------------------------------------------------------------------- 1 | @color1: #ffffff; 2 | -------------------------------------------------------------------------------- /test/fixtures/libraries/lib1/my/ui/lib/themes/foo/library.source.less: -------------------------------------------------------------------------------- 1 | @import "../base/library.source.less"; 2 | @import "global.less"; 3 | -------------------------------------------------------------------------------- /test/fixtures/libraries/lib1/sap/ui/core/themes/foo/.theming: -------------------------------------------------------------------------------- 1 | { 2 | "mCssScopes": { 3 | "library": { 4 | "sBaseFile": "library", 5 | "aScopes": [ 6 | { 7 | "sSelector": "fooContrast", 8 | "sEmbeddedFile": "bar.library", 9 | "sEmbeddedCompareFile": "library" 10 | } 11 | ] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/fixtures/libraries/lib2/my/ui/lib/themes/bar/global.less: -------------------------------------------------------------------------------- 1 | @color1: #000000; 2 | -------------------------------------------------------------------------------- /test/fixtures/libraries/lib2/my/ui/lib/themes/bar/library.source.less: -------------------------------------------------------------------------------- 1 | @import "../foo/library.source.less"; 2 | @import "global.less"; 3 | -------------------------------------------------------------------------------- /test/fixtures/libraries/lib2/sap/ui/core/themes/bar/.theming: -------------------------------------------------------------------------------- 1 | { 2 | "mCssScopes": { 3 | "library": { 4 | "sBaseFile": "foo.library", 5 | "aScopes": [ 6 | { 7 | "sSelector": "barContrast", 8 | "sEmbeddedFile": "library", 9 | "sEmbeddedCompareFile": "foo.library" 10 | } 11 | ] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/fixtures/libraries/lib3/my/other/ui/lib/themes/bar/library.source.less: -------------------------------------------------------------------------------- 1 | @import "../foo/library.source.less"; 2 | @import "../../../../../../my/ui/lib/themes/bar/global.less"; 3 | -------------------------------------------------------------------------------- /test/fixtures/libraries/lib3/my/other/ui/lib/themes/base/MyControl.less: -------------------------------------------------------------------------------- 1 | @_my_other_ui_lib_MyControl_color1: @color1; 2 | 3 | .myOtherUiLibRule1 { 4 | color: @_my_other_ui_lib_MyControl_color1; 5 | } 6 | 7 | .myOtherUiLibRule2 { 8 | padding: 1px 2px 3px 4px; 9 | } 10 | -------------------------------------------------------------------------------- /test/fixtures/libraries/lib3/my/other/ui/lib/themes/base/library.source.less: -------------------------------------------------------------------------------- 1 | @import "../../../../../../my/ui/lib/themes/base/global.less"; 2 | @import "MyControl.less"; 3 | @import "sub-directory/MyOtherControl.less"; 4 | -------------------------------------------------------------------------------- /test/fixtures/libraries/lib3/my/other/ui/lib/themes/base/sub-directory/MyOtherControl.less: -------------------------------------------------------------------------------- 1 | @_my_other_ui_lib_MyOtherControl_color1: @color1; 2 | -------------------------------------------------------------------------------- /test/fixtures/libraries/lib3/my/other/ui/lib/themes/foo/MyControl.less: -------------------------------------------------------------------------------- 1 | @_my_other_ui_lib_MyControl_color1: @color1; 2 | @_my_other_ui_lib_MyControl_color2: green; 3 | -------------------------------------------------------------------------------- /test/fixtures/libraries/lib3/my/other/ui/lib/themes/foo/library.source.less: -------------------------------------------------------------------------------- 1 | @import "../base/library.source.less"; 2 | @import "../../../../../../my/ui/lib/themes/foo/global.less"; 3 | @import "MyControl.less"; 4 | -------------------------------------------------------------------------------- /test/fixtures/libraries/lib4/my/ui/lib/themes/cascading/indirect-a.less: -------------------------------------------------------------------------------- 1 | @import "variables.less"; 2 | @import "override.less"; -------------------------------------------------------------------------------- /test/fixtures/libraries/lib4/my/ui/lib/themes/cascading/indirect-b.less: -------------------------------------------------------------------------------- 1 | @import "variables.less"; -------------------------------------------------------------------------------- /test/fixtures/libraries/lib4/my/ui/lib/themes/cascading/library.source.less: -------------------------------------------------------------------------------- 1 | @import "indirect-a.less"; 2 | @import "indirect-b.less"; 3 | 4 | body { 5 | color: @color; 6 | } -------------------------------------------------------------------------------- /test/fixtures/libraries/lib4/my/ui/lib/themes/cascading/override.less: -------------------------------------------------------------------------------- 1 | @color: black; 2 | -------------------------------------------------------------------------------- /test/fixtures/libraries/lib4/my/ui/lib/themes/cascading/variables.less: -------------------------------------------------------------------------------- 1 | @color: red; 2 | -------------------------------------------------------------------------------- /test/fixtures/libraries/lib5/my/ui/lib/themes/cascading/base/library.source.less: -------------------------------------------------------------------------------- 1 | body { 2 | color: black; 3 | } 4 | 5 | a { 6 | color: black; 7 | } 8 | 9 | b { 10 | text-align: center; 11 | } 12 | -------------------------------------------------------------------------------- /test/fixtures/libraries/lib5/my/ui/lib/themes/cascading/library.source.less: -------------------------------------------------------------------------------- 1 | @import "base/library.source.less"; 2 | @import "plus/library.source.less"; 3 | -------------------------------------------------------------------------------- /test/fixtures/libraries/lib5/my/ui/lib/themes/cascading/plus/library.source.less: -------------------------------------------------------------------------------- 1 | body { 2 | color: blue; 3 | } 4 | /* ============================= */ 5 | /* Shared CSS 1 theme */ 6 | /* ============================= */ 7 | #CSS_SCOPE_ROOT { 8 | color: orange; 9 | } 10 | /* ============================= */ 11 | /* Shared CSS 2 theme */ 12 | /* ============================= */ 13 | a { 14 | color: blue; 15 | } 16 | 17 | b { 18 | text-align: center; 19 | } 20 | -------------------------------------------------------------------------------- /test/fixtures/libraries/scopes/comments/lib1/comments/themes/foo/library.source.less: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a sample comment! 3 | */ 4 | 5 | .myRule1 { 6 | /** 7 | * This is a sample comment inside a rule! 8 | */ 9 | color: #000000; 10 | } -------------------------------------------------------------------------------- /test/fixtures/libraries/scopes/comments/lib2/comments/themes/bar/library.source.less: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a sample comment! 3 | */ 4 | 5 | .myRule1 { 6 | /** 7 | * This is a sample comment inside a rule 8 | * with a different color value! 9 | */ 10 | color: #ffffff; 11 | } -------------------------------------------------------------------------------- /test/fixtures/libraries/scopes/comments/lib3/comments/themes/bar/library.source.less: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a sample comment! 3 | */ 4 | 5 | .myRule1 { 6 | /** 7 | * This is a sample comment inside a rule 8 | * with a different color value! 9 | */ 10 | color: #ffffff; 11 | } 12 | 13 | @import "/lib3/comments/themes/other/sub/my.less"; 14 | @import "./my2.less"; 15 | @import "../my3.less"; -------------------------------------------------------------------------------- /test/fixtures/libraries/scopes/comments/lib3/comments/themes/bar/my2.less: -------------------------------------------------------------------------------- 1 | a { 2 | color: green; 3 | } -------------------------------------------------------------------------------- /test/fixtures/libraries/scopes/comments/lib3/comments/themes/my3.less: -------------------------------------------------------------------------------- 1 | html { 2 | color: green; 3 | } -------------------------------------------------------------------------------- /test/fixtures/libraries/scopes/comments/lib3/comments/themes/other/sub/my.less: -------------------------------------------------------------------------------- 1 | b { 2 | color: green; 3 | } -------------------------------------------------------------------------------- /test/fixtures/libraries/scopes/css-scope-root/lib1/css-scope-root/themes/foo/library.source.less: -------------------------------------------------------------------------------- 1 | .myRule1 { 2 | color: #000000; 3 | } 4 | 5 | .myRule2 { 6 | color: #c3c3c3; 7 | } -------------------------------------------------------------------------------- /test/fixtures/libraries/scopes/css-scope-root/lib2/css-scope-root/themes/bar/library.source.less: -------------------------------------------------------------------------------- 1 | .myRule1 { 2 | color: #ffffff; 3 | } 4 | 5 | #CSS_SCOPE_ROOT { 6 | color: #aaaaaa; 7 | } 8 | 9 | .myRule2 { 10 | color: #444444; 11 | } -------------------------------------------------------------------------------- /test/fixtures/libraries/scopes/default/lib1/default/themes/foo/library.source.less: -------------------------------------------------------------------------------- 1 | .myRule1 { 2 | color: #000000; 3 | } 4 | 5 | .myRule2 { 6 | color: #ffffff; 7 | float: left; 8 | } 9 | 10 | .myRule3 { 11 | display: -webkit-flex; 12 | display: flex; 13 | } 14 | 15 | .myRule4 { 16 | display: -webkit-box; 17 | display: -moz-box; 18 | display: -ms-flexbox; 19 | display: flex; 20 | } 21 | 22 | .myRule5, .myRule6, .myRule7 { 23 | color: #000000; 24 | } 25 | 26 | .myRule8 { 27 | display: block; 28 | } 29 | 30 | .sapContrast { 31 | .sapMNLI { 32 | background: #ffffff; 33 | } 34 | .sapMNLG-GroupHeader { 35 | background: #000000; 36 | } 37 | 38 | .sapMNLG-Body { 39 | .sapMNLI { 40 | background: darken(#ffffff,5); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /test/fixtures/libraries/scopes/default/lib2/default/themes/bar/library.source.less: -------------------------------------------------------------------------------- 1 | .myRule1 { 2 | color: #ffffff; 3 | } 4 | 5 | .myRule2 { 6 | color: #ffffff; 7 | float: left; 8 | } 9 | 10 | .myRule3 { 11 | display: -webkit-flex; 12 | display: flex; 13 | } 14 | 15 | .myRule4 { 16 | display: -webkit-box; 17 | display: -moz-box; 18 | display: -ms-flexbox; 19 | display: flex; 20 | } 21 | 22 | .myRule5, .myRule6, .myRule7 { 23 | color: #ffffff; 24 | } 25 | 26 | .myRule8 { 27 | display: block; 28 | } 29 | 30 | .sapContrast { 31 | .sapMNLI { 32 | background: #000000; 33 | } 34 | .sapMNLG-GroupHeader { 35 | background: #000000; 36 | } 37 | 38 | .sapMNLG-Body { 39 | .sapMNLI { 40 | background: darken(#000000,5); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /test/fixtures/libraries/scopes/dom/lib1/dom/themes/foo/library.source.less: -------------------------------------------------------------------------------- 1 | div.myRule1 { 2 | color: #000000; 3 | } 4 | 5 | div.myRule1 > .myRule2 { 6 | color: #000000; 7 | } 8 | -------------------------------------------------------------------------------- /test/fixtures/libraries/scopes/dom/lib2/dom/themes/bar/library.source.less: -------------------------------------------------------------------------------- 1 | div.myRule1 { 2 | color: #ffffff; 3 | } 4 | 5 | div.myRule1 > .myRule2 { 6 | color: #ffffff; 7 | } 8 | -------------------------------------------------------------------------------- /test/fixtures/libraries/scopes/empty-media-queries/lib1/empty-media-queries/themes/foo/library.source.less: -------------------------------------------------------------------------------- 1 | .myRule1 { 2 | color: #000000; 3 | } 4 | 5 | @media (min-width: 600px) and (max-width: 1023px) { 6 | .myRule1 { 7 | width: 100px; 8 | } 9 | } -------------------------------------------------------------------------------- /test/fixtures/libraries/scopes/empty-media-queries/lib2/empty-media-queries/themes/bar/library.source.less: -------------------------------------------------------------------------------- 1 | .myRule1 { 2 | color: #ffffff; 3 | } 4 | 5 | @media (min-width: 600px) and (max-width: 1023px) { 6 | .myRule1 { 7 | width: 100px; 8 | } 9 | } -------------------------------------------------------------------------------- /test/fixtures/libraries/scopes/html/lib1/html/themes/foo/library.source.less: -------------------------------------------------------------------------------- 1 | html[dir=rtl][data-sap-ui-browser^=ie] .sapUiGrid th > .sapUiFormTitle, 2 | html[dir=rtl][data-sap-ui-browser^=ie] .sapUiGrid td > .sapUiFormTitle, 3 | html[dir=rtl][data-sap-ui-browser^=ed] .sapUiGrid th > .sapUiFormTitle, 4 | html[dir=rtl][data-sap-ui-browser^=ed] .sapUiGrid td > .sapUiFormTitle { 5 | color: #000000; 6 | } 7 | html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid th > .sapUiFormTitle, 8 | html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid td > .sapUiFormTitle, 9 | html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid th > .sapUiFormTitle, 10 | html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid td > .sapUiFormTitle { 11 | color: #000000; 12 | } 13 | 14 | html[dir=rtl][data-sap-ui-browser^=ie].sap-desktop-sapUiClass .testClass { 15 | color: #000000; 16 | } -------------------------------------------------------------------------------- /test/fixtures/libraries/scopes/html/lib2/html/themes/bar/library.source.less: -------------------------------------------------------------------------------- 1 | html[dir=rtl][data-sap-ui-browser^=ie] .sapUiGrid th > .sapUiFormTitle, 2 | html[dir=rtl][data-sap-ui-browser^=ie] .sapUiGrid td > .sapUiFormTitle, 3 | html[dir=rtl][data-sap-ui-browser^=ed] .sapUiGrid th > .sapUiFormTitle, 4 | html[dir=rtl][data-sap-ui-browser^=ed] .sapUiGrid td > .sapUiFormTitle { 5 | color: #ffffff; 6 | } 7 | html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid th > .sapUiFormTitle, 8 | html[dir=rtl][data-sap-ui-browser^='ie'].sapUiGrid td > .sapUiFormTitle, 9 | html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid th > .sapUiFormTitle, 10 | html[dir=rtl][data-sap-ui-browser^='ed'].sapUiGrid td > .sapUiFormTitle { 11 | color: #ffffff; 12 | } 13 | 14 | html[dir=rtl][data-sap-ui-browser^=ie].sap-desktop-sapUiClass .testClass { 15 | color: #ffffff; 16 | } -------------------------------------------------------------------------------- /test/fixtures/libraries/scopes/media-queries/lib1/media-queries/themes/foo/library.source.less: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'myfont'; 3 | src: url('font.eot'); 4 | font-weight: normal; 5 | font-style: normal; 6 | font-stretch: normal; 7 | } 8 | 9 | @media (min-width: 600px) and (max-width: 1023px) { 10 | .myRule1 { 11 | color: #000000; 12 | } 13 | 14 | .myRule2 { 15 | color: #ffffff; 16 | } 17 | 18 | .myRule3 { 19 | display: -webkit-flex; 20 | display: flex; 21 | } 22 | 23 | .sapMALI { 24 | display: -webkit-box; 25 | display: -moz-box; 26 | display: -ms-flexbox; 27 | display: flex; 28 | } 29 | 30 | .sapMALI .sapMLIBUnread, 31 | .sapMALI .sapMLIBSelectS, 32 | .sapMALI .sapMLIBSelectM, 33 | .sapMALI .sapMLIBSelectD { 34 | display: none; 35 | } 36 | 37 | .sapMALI { 38 | height: 3rem; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /test/fixtures/libraries/scopes/media-queries/lib2/media-queries/themes/bar/library.source.less: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'myfont'; 3 | src: url('font.eot'); 4 | font-weight: normal; 5 | font-style: normal; 6 | font-stretch: normal; 7 | } 8 | 9 | @media (min-width: 600px) and (max-width: 1023px) { 10 | .myRule1 { 11 | color: #ffffff; 12 | } 13 | 14 | .myRule2 { 15 | color: #ffffff; 16 | } 17 | 18 | .myRule3 { 19 | display: -webkit-flex; 20 | display: flex; 21 | } 22 | 23 | .sapMALI { 24 | display: -webkit-box; 25 | display: -moz-box; 26 | display: -ms-flexbox; 27 | display: flex; 28 | } 29 | 30 | .sapMALI .sapMLIBUnread, 31 | .sapMALI .sapMLIBSelectS, 32 | .sapMALI .sapMLIBSelectM, 33 | .sapMALI .sapMLIBSelectD { 34 | display: none; 35 | } 36 | 37 | .sapMALI { 38 | height: 3rem; 39 | } 40 | } -------------------------------------------------------------------------------- /test/fixtures/libraries/scopes/mixins/lib1/mixins/themes/foo/library.source.less: -------------------------------------------------------------------------------- 1 | @media screen and (min-width: 600px) { 2 | body { 3 | background: #000000; 4 | font-size: 21px; 5 | } 6 | 7 | html.myRule1 td { 8 | color: #000000; 9 | } 10 | } -------------------------------------------------------------------------------- /test/fixtures/libraries/scopes/mixins/lib2/mixins/themes/bar/library.source.less: -------------------------------------------------------------------------------- 1 | @media screen and (min-width: 600px) { 2 | body { 3 | background: #ffffff; 4 | font-size: 20px; 5 | } 6 | 7 | html.myRule1 td { 8 | color: #ffffff; 9 | } 10 | } 11 | 12 | .myRule2 { 13 | background: #ffffff; 14 | } 15 | 16 | @media screen and (min-width: 600px) { 17 | body { 18 | background: #ffffff; 19 | font-size: 20px; 20 | } 21 | 22 | .myRule2 { 23 | background: #ffffff; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/fixtures/libraries/scopes/multiple-imports/lib1/multiple-imports/themes/foo/library.source.less: -------------------------------------------------------------------------------- 1 | .myRule1 { 2 | color: #ffffff; 3 | } 4 | 5 | .myRule2 { 6 | display: block; 7 | } 8 | 9 | .myRule5 { 10 | color: #ffffff; 11 | } 12 | 13 | .myRule6 { 14 | display: block; 15 | } 16 | -------------------------------------------------------------------------------- /test/fixtures/libraries/scopes/multiple-imports/lib2/multiple-imports/themes/bar/library.source.less: -------------------------------------------------------------------------------- 1 | .myRule1 { 2 | color: #ffffff; 3 | } 4 | 5 | .myRule2 { 6 | display: block; 7 | } 8 | 9 | .myRule3 { 10 | color: red; 11 | } 12 | 13 | .myRule4 { 14 | background-color: transparent !important; 15 | } 16 | 17 | .myRule5 { 18 | color: #ffffff; 19 | } 20 | 21 | .myRule6 { 22 | display: block; 23 | } 24 | 25 | .myRule7 { 26 | color: red; 27 | } 28 | -------------------------------------------------------------------------------- /test/fixtures/rootPaths/lib1/my/themes/foo/foo.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP/less-openui5/382f8fba664b7c6d298e0762d5167cf31e4836ef/test/fixtures/rootPaths/lib1/my/themes/foo/foo.less -------------------------------------------------------------------------------- /test/fixtures/rootPaths/lib2/my/themes/bar/bar.less: -------------------------------------------------------------------------------- 1 | @import "../foo/foo.less"; 2 | -------------------------------------------------------------------------------- /test/fixtures/rtl/background-position.less: -------------------------------------------------------------------------------- 1 | /* RTL background-position mirroring */ 2 | .rtlBackgroundPositionMirroring { 3 | background-position: top; 4 | background-position: right; 5 | background-position: left top; 6 | 7 | // no mirroring of absolute sizes because they cannot be set from the right border in CSS2.1 */ 8 | background-position: 2cm 1cm; 9 | 10 | background-position: 20% 40%; 11 | background-position: 30%; 12 | background-position: 0.5%; 13 | background-position: -20% 40%; 14 | background-position: 0% 0; 15 | background-position: 0 0; 16 | background-position: 100% 0; 17 | background-position: 0 100%; 18 | background-position: 100% /* some comment */ 100%; 19 | 20 | // multiple backgrounds 21 | background-position: top, top; 22 | background-position: left top, left top, center bottom; 23 | 24 | // no mirroring of absolute sizes because they cannot be set from the right border in CSS2.1 25 | background-position: 2cm 1cm,3cm 4cm; 26 | 27 | background-position: 20% 40%, 10% 90%,33% 66%; 28 | background-position: /* some comment */ 30%, 60%; 29 | 30 | // mixed units 31 | background-position: 20em 40%, 10px 90%,33% 66%; 32 | 33 | // negative values 34 | background-position: -30%; 35 | background-position: -20% /* some comment */ -40%, /* some comment */ -5% 30%, -10% 80%; 36 | } 37 | 38 | .rtlBackgroundPositionMirroringMixIn(@num) { 39 | .rtlBackgroundPositionMirroring@{num} { 40 | background-position: top; 41 | background-position: right; 42 | background-position: left top; 43 | 44 | // no mirroring of absolute sizes because they cannot be set from the right border in CSS2.1 */ 45 | background-position: 2cm 1cm; 46 | 47 | background-position: 20% 40%; 48 | background-position: 30%; 49 | background-position: 0.5%; 50 | background-position: -20% 40%; 51 | background-position: 0% 0; 52 | background-position: 0 0; 53 | background-position: 100% 0; 54 | background-position: 0 100%; 55 | background-position: 100% /* some comment */ 100%; 56 | 57 | // multiple backgrounds 58 | background-position: top, top; 59 | background-position: left top, left top, center bottom; 60 | 61 | // no mirroring of absolute sizes because they cannot be set from the right border in CSS2.1 62 | background-position: 2cm 1cm,3cm 4cm; 63 | 64 | background-position: 20% 40%, 10% 90%,33% 66%; 65 | background-position: /* some comment */ 30%, 60%; 66 | 67 | // mixed units 68 | background-position: 20em 40%, 10px 90%,33% 66%; 69 | 70 | // negative values 71 | background-position: -30%; 72 | background-position: -20% /* some comment */ -40%, /* some comment */ -5% 30%, -10% 80%; 73 | } 74 | } 75 | .rtlBackgroundPositionMirroringMixIn(1); 76 | .rtlBackgroundPositionMirroringMixIn(2); -------------------------------------------------------------------------------- /test/fixtures/rtl/background.less: -------------------------------------------------------------------------------- 1 | /* RTL background mirroring */ 2 | .rtlBackgroundMirroring { 3 | background: red; 4 | background: url("chess.png") 40% e("/") 10em #808080 round fixed border-box /* some comment */; 5 | background: url("img/column_header.gif") repeat scroll 0 0 #d2dae8; 6 | background: url("img/column_header.gif") repeat scroll 10% 0 #d2dae8; 7 | background: url("img/column_header.gif") repeat scroll 0 10% #d2dae8; 8 | background: url("chess.png") left top / 10% #808080 round fixed border-box; 9 | background: url("chess.png") left 10% e("/") 10% #808080 round fixed border-box; 10 | background: url("chess.png") -10% -10% e("/") 10% #808080 round fixed border-box; 11 | background: -10% -10% e("/") 10% url("chess.png") #808080 round fixed border-box; 12 | background: url('img/drop-down_ico.png') no-repeat bottom right, url('img/hover_column_header.gif'); 13 | } 14 | 15 | .rtlBackgroundMixIn(@class) { 16 | .@{class} { 17 | background: red; 18 | background: url("chess.png") 40% e("/") 10em #808080 round fixed border-box /* some comment */; 19 | background: url("./img/column_header.gif") repeat scroll 0 0 #d2dae8; 20 | background: url("../rtl/img/column_header.gif") repeat scroll 10% 0 #d2dae8; 21 | background: url("img/column_header.gif") repeat scroll 0 10% #d2dae8; 22 | background: url("chess.png") left top / 10% #808080 round fixed border-box; 23 | background: url("chess.png") left 10% e("/") 10% #808080 round fixed border-box; 24 | background: url("chess.png") -10% -10% e("/") 10% #808080 round fixed border-box; 25 | background: -10% -10% e("/") 10% url("chess.png") #808080 round fixed border-box; 26 | background: url('img/drop-down_ico.png') no-repeat bottom right, url('img/hover_column_header.gif'); 27 | } 28 | } 29 | .rtlBackgroundMixIn(rtlBackgroundMirroringWithMixIn1); 30 | .rtlBackgroundMixIn(rtlBackgroundMirroringWithMixIn2); 31 | 32 | /* RTL background-image mirroring */ 33 | .rtlBackgroundImageMirroring { 34 | 35 | // relative 36 | background-image: url(img/column_header.gif); 37 | background-image: url('img/column_header.gif'); 38 | background-image: url("img/column_header.gif") /* some comment */; 39 | 40 | // absolute 41 | background-image: url(http://www.x.de/img/column_header.gif); 42 | background-image: /* some comment */ url('http://www.x.de/img/column_header.gif'); 43 | background-image: url("http://www.x.de/img/column_header.gif"); 44 | 45 | // multiple 46 | background-image: url(img/column_header.gif), /* some comment */ url(img/column_header2.gif); 47 | background-image: url("http://www.x.de/img/column_header.gif"), url(img/column_header2.gif); 48 | background-image: url(img/column_header.gif), url(img/column_header2.gif), url(img/column_header3.gif); 49 | 50 | } 51 | 52 | .rtlBackgroundImageMixIn(@class) { 53 | .@{class} { 54 | // relative 55 | background-image: url(img/column_header.gif); 56 | background-image: url('img/column_header.gif'); 57 | background-image: url("img/column_header.gif") /* some comment */; 58 | 59 | // absolute 60 | background-image: url(http://www.x.de/img/column_header.gif); 61 | background-image: /* some comment */ url('http://www.x.de/img/column_header.gif'); 62 | background-image: url("http://www.x.de/img/column_header.gif"); 63 | 64 | // multiple 65 | background-image: url(img/column_header.gif), /* some comment */ url(img/column_header2.gif); 66 | background-image: url("http://www.x.de/img/column_header.gif"), url(img/column_header2.gif); 67 | background-image: url(img/column_header.gif), url(img/column_header2.gif), url(img/column_header3.gif); 68 | } 69 | } 70 | .rtlBackgroundImageMixIn(rtlBackgroundImageMirroringWithMixIn1); 71 | .rtlBackgroundImageMixIn(rtlBackgroundImageMirroringWithMixIn2); 72 | -------------------------------------------------------------------------------- /test/fixtures/rtl/cursor.less: -------------------------------------------------------------------------------- 1 | /* RTL cursor mirroring */ 2 | .rtlCursorMirroring-auto { 3 | cursor: auto; 4 | } 5 | .rtlCursorMirroring-default { 6 | cursor: default; 7 | } 8 | .rtlCursorMirroring-pointer { 9 | cursor: pointer; 10 | } 11 | .rtlCursorMirroring-e-resize { 12 | cursor: /* some comment */ e-resize; 13 | } 14 | .rtlCursorMirroring-w-resize { 15 | cursor: w-resize; 16 | } 17 | .rtlCursorMirroring-ne-resize { 18 | cursor: ne-resize /* some comment */; 19 | } 20 | .rtlCursorMirroring-nw-resize { 21 | cursor: nw-resize; 22 | } 23 | .rtlCursorMirroring-se-resize { 24 | cursor: se-resize; 25 | } 26 | .rtlCursorMirroring-sw-resize { 27 | cursor: sw-resize; 28 | } 29 | .rtlCursorMirroring-nesw-resize { 30 | cursor: nesw-resize; 31 | } 32 | .rtlCursorMirroring-nwse-resize { 33 | cursor: nwse-resize; 34 | } 35 | .rtlCursorMirroring-url-auto { 36 | cursor: url(something), auto; 37 | } 38 | .rtlCursorMirroring-url-e-resize { 39 | cursor: /* some comment */ url(something), e-resize; 40 | } 41 | .rtlCursorMirroring-url-url-w-resize { 42 | cursor: url(something), /* some comment */ url(something2), w-resize; 43 | } 44 | 45 | .rtlCursorMirroringMixIn(@num) { 46 | .rtlCursorMirroring-auto@{num} { 47 | cursor: auto; 48 | } 49 | .rtlCursorMirroring-default@{num} { 50 | cursor: default; 51 | } 52 | .rtlCursorMirroring-pointer@{num} { 53 | cursor: pointer; 54 | } 55 | .rtlCursorMirroring-e-resize@{num} { 56 | cursor: /* some comment */ e-resize; 57 | } 58 | .rtlCursorMirroring-w-resize@{num} { 59 | cursor: w-resize; 60 | } 61 | .rtlCursorMirroring-ne-resize@{num} { 62 | cursor: ne-resize /* some comment */; 63 | } 64 | .rtlCursorMirroring-nw-resize@{num} { 65 | cursor: nw-resize; 66 | } 67 | .rtlCursorMirroring-se-resize@{num} { 68 | cursor: se-resize; 69 | } 70 | .rtlCursorMirroring-sw-resize@{num} { 71 | cursor: sw-resize; 72 | } 73 | .rtlCursorMirroring-nesw-resize@{num} { 74 | cursor: nesw-resize; 75 | } 76 | .rtlCursorMirroring-nwse-resize@{num} { 77 | cursor: nwse-resize; 78 | } 79 | .rtlCursorMirroring-url-auto@{num} { 80 | cursor: url(something), auto; 81 | } 82 | .rtlCursorMirroring-url-e-resize@{num} { 83 | cursor: /* some comment */ url(something), e-resize; 84 | } 85 | .rtlCursorMirroring-url-url-w-resize@{num} { 86 | cursor: url(something), /* some comment */ url(something2), w-resize; 87 | } 88 | } 89 | .rtlCursorMirroringMixIn(1); 90 | .rtlCursorMirroringMixIn(2); 91 | -------------------------------------------------------------------------------- /test/fixtures/rtl/image-url.less: -------------------------------------------------------------------------------- 1 | /* RTL image url mirroring */ 2 | .rtlImageUrlMirroringRelative { 3 | background: url(img/fg/hi.png) /* some comment */; 4 | background-image: /* some comment */ url(img/fg/hi.png); 5 | list-style-image: url(img/fg/hi.png); 6 | } 7 | .rtlImageUrlMirroringAbsolute { 8 | background: url(http://abc.de/img/fg/hi.png) /* some comment */; 9 | } 10 | .rtlImageUrlMirroringQuotes { 11 | background: url('http://abc.de/img/fg/hi.png'); 12 | background: /* some comment */ url("http://abc.de/img/fg/hi.png"); 13 | 14 | background: url('img/column_header.gif') repeat /* some comment */ scroll 0 0 #d2dae8; 15 | background: url("img/column_header.gif") repeat scroll 0 0 #d2dae8 /* some comment */; 16 | } 17 | .rtlImageUrlMirroringMultiple { 18 | background: url(http://abc.de/img/fg/hi.png), /* some comment */ url(http://abc.de/img/fg/hi.png); 19 | } 20 | 21 | .rtlImageUrlMirroringMixIn(@num) { 22 | .rtlImageUrlMirroringRelative@{num} { 23 | background: url(img/fg/hi.png) /* some comment */; 24 | background-image: /* some comment */ url(img/fg/hi.png); 25 | list-style-image: url(img/fg/hi.png); 26 | } 27 | .rtlImageUrlMirroringAbsolute@{num} { 28 | background: url(http://abc.de/img/fg/hi.png) /* some comment */; 29 | } 30 | .rtlImageUrlMirroringQuotes@{num} { 31 | background: url('http://abc.de/img/fg/hi.png'); 32 | background: /* some comment */ url("http://abc.de/img/fg/hi.png"); 33 | 34 | background: url('img/column_header.gif') repeat /* some comment */ scroll 0 0 #d2dae8; 35 | background: url("img/column_header.gif") repeat scroll 0 0 #d2dae8 /* some comment */; 36 | } 37 | .rtlImageUrlMirroringMultiple@{num} { 38 | background: url(http://abc.de/img/fg/hi.png), /* some comment */ url(http://abc.de/img/fg/hi.png); 39 | } 40 | } 41 | .rtlImageUrlMirroringMixIn(1); 42 | .rtlImageUrlMirroringMixIn(2); 43 | -------------------------------------------------------------------------------- /test/fixtures/rtl/img-RTL/column_header.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP/less-openui5/382f8fba664b7c6d298e0762d5167cf31e4836ef/test/fixtures/rtl/img-RTL/column_header.gif -------------------------------------------------------------------------------- /test/fixtures/rtl/img-RTL/column_header2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP/less-openui5/382f8fba664b7c6d298e0762d5167cf31e4836ef/test/fixtures/rtl/img-RTL/column_header2.gif -------------------------------------------------------------------------------- /test/fixtures/rtl/img-RTL/column_header3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP/less-openui5/382f8fba664b7c6d298e0762d5167cf31e4836ef/test/fixtures/rtl/img-RTL/column_header3.gif -------------------------------------------------------------------------------- /test/fixtures/rtl/img-RTL/drop-down_ico.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP/less-openui5/382f8fba664b7c6d298e0762d5167cf31e4836ef/test/fixtures/rtl/img-RTL/drop-down_ico.png -------------------------------------------------------------------------------- /test/fixtures/rtl/img-RTL/fg/hi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP/less-openui5/382f8fba664b7c6d298e0762d5167cf31e4836ef/test/fixtures/rtl/img-RTL/fg/hi.png -------------------------------------------------------------------------------- /test/fixtures/rtl/img-RTL/hover_column_header.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP/less-openui5/382f8fba664b7c6d298e0762d5167cf31e4836ef/test/fixtures/rtl/img-RTL/hover_column_header.gif -------------------------------------------------------------------------------- /test/fixtures/rtl/shadow.less: -------------------------------------------------------------------------------- 1 | /* RTL box-shadow mirroring */ 2 | .rtlBoxShadowMirroring { 3 | // no modifications 4 | box-shadow: none; 5 | box-shadow: inherit; 6 | // simple shadows 7 | box-shadow: 1px 2px; 8 | box-shadow: -5cm 2px; 9 | box-shadow: 1px 2px red; 10 | box-shadow: 1px 2px rgb(1,2,3); 11 | box-shadow: 2px -20px 30px 2px rgba(0,0,0,0.15); 12 | text-shadow: -1em -0.0625rem 0 rgba(0,0,0,0.6); 13 | text-shadow: 0 -0.0625rem 0 rgba(0,0,0,0.6); 14 | // multiple shadows 15 | box-shadow: /* some comment */ 1px 2px,3px 4px; 16 | box-shadow: 1px 2px,3px 4px, -5px 6px; 17 | box-shadow: 1px 2px 10px,3px 4px 20px /* some comment */; 18 | box-shadow: 1px -2cm 10px 2ex,-3px 4em 20px red; 19 | box-shadow: 1px -2cm 10px 2ex #00FFAC,-3px 4em 20px rgb(12,33,42); 20 | // with inset 21 | box-shadow: /* some comment */ inset 1px 2px; 22 | box-shadow: inset -5cm 2px; 23 | box-shadow: inset 1px 2px red; 24 | box-shadow: inset 1px 2px rgb(1,2,3); 25 | box-shadow: inset /* some comment */ 1px 2px,3px 4px; 26 | box-shadow: 1px 2px,inset 3px 4px, -5px 6px; 27 | box-shadow: inset 1px -2cm 10px 2ex #00FFAC,inset -3px 4em 20px rgb(12,33,42); 28 | // cross-browser 29 | -moz-box-shadow: 1px 2px; 30 | -webkit-box-shadow: 1px 2px; 31 | // actual examples 32 | box-shadow: 5px 5px 10px rgba(0,0,0,0.5), inset 2px 3px; 33 | -moz-box-shadow: 5px 5px 10px rgba(0,0,0,0.5), inset 2px 3px; 34 | -webkit-box-shadow: 5px 5px 10px rgba(0,0,0,0.5), inset 2px 3px; 35 | } 36 | 37 | .rtlBoxShadowMixIn(@num) { 38 | .rtlBoxShadowMirroring@{num} { 39 | box-shadow: inset 1px 1px 0px 0px #000000, inset -1px -1px 0px 0px #123456, inset 0 -0.1875rem 0 #333333, inset 0 -0.25rem #000000; 40 | background: url("chess.png") left top / 10% #808080 round fixed border-box; 41 | right: 0; 42 | } 43 | } 44 | .rtlBoxShadowMixIn(1); 45 | .rtlBoxShadowMixIn(2); 46 | 47 | /* RTL text-shadow mirroring */ 48 | .rtlTextShadowMirroring { 49 | // no modifications 50 | text-shadow: none; 51 | text-shadow: inherit; 52 | // simple shadows 53 | text-shadow: 1px /* some comment */ 2px; 54 | text-shadow: -5cm 2px; 55 | text-shadow: 1px 2px /* some comment */ red; 56 | text-shadow: 1px 2px rgb(1,2,3); 57 | // multiple shadows 58 | text-shadow: 1px 2px,3px 4px; 59 | text-shadow: 1px 2px,3px /* some comment */ 4px, -5px 6px /* some comment */; 60 | } 61 | -------------------------------------------------------------------------------- /test/fixtures/rtl/variables.less: -------------------------------------------------------------------------------- 1 | /* Variables */ 2 | 3 | @var1: #123456; 4 | @_PRIVATE_var2: #111111; 5 | 6 | .variables { 7 | color: @var1; 8 | background-color: @_PRIVATE_var2; 9 | } 10 | -------------------------------------------------------------------------------- /test/fixtures/simple/test.less: -------------------------------------------------------------------------------- 1 | @myColor: #ff0000; 2 | @myFontFamily: "MyFont"; 3 | @myFontUrl: url("./MyFont.woff2"); 4 | @myScale1: scale(1); 5 | @myScale2: scale(2); 6 | 7 | .myRule { 8 | float: left; 9 | color: @myColor; 10 | } 11 | 12 | @keyframes myKeyframes { 13 | 0%, 80%, 100% { 14 | transform: @myScale1; 15 | } 16 | 40% { 17 | transform: @myScale2; 18 | } 19 | } 20 | 21 | @font-face { 22 | font-family: @myFontFamily; 23 | src: @myFontUrl format("woff2"); 24 | } 25 | -------------------------------------------------------------------------------- /test/fixtures/variables/main.less: -------------------------------------------------------------------------------- 1 | @myVar: #ffffff; 2 | @myVar2: darken(@myVar, 50%); 3 | 4 | .myMixIn(@color) { 5 | @myVar: darken(@color, 50%); 6 | @myPrivateVar: darken(@color, 20%); 7 | 8 | .myNestedMixIn() { 9 | @myPrivateVar: @myVar; 10 | color: @myPrivateVar; 11 | } 12 | 13 | .myNestedMixIn; 14 | background-color: @myPrivateVar; 15 | } 16 | 17 | @myOtherPublicVar: 'test'; 18 | 19 | .myRule { 20 | .myMixIn(@myVar); 21 | } 22 | 23 | .myRule2 { 24 | background-color: @myVar; 25 | color: @myVar2; 26 | } 27 | -------------------------------------------------------------------------------- /test/lib/Compiler.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | const assert = require("assert"); 4 | const sinon = require("sinon"); 5 | 6 | // tested module 7 | const Compiler = require("../../lib/Compiler"); 8 | 9 | describe("Compiler#createFileHandler", function() { 10 | before(() => { 11 | sinon.stub(console, "log"); 12 | }); 13 | after(() => { 14 | sinon.restore(); 15 | }); 16 | 17 | it("should propagate errors via callback function (TypeError)", async function() { 18 | const compiler = new Compiler({ 19 | options: { 20 | rootPaths: ["foo"] 21 | }, 22 | fileUtils: undefined // This will cause a TypeError when calling fileUtils.readFile 23 | }); 24 | 25 | const file = "someFile"; 26 | const currentFileInfo = { 27 | currentDirectory: "someFolder" 28 | }; 29 | const handleDataAndCallCallback = sinon.stub(); 30 | const callback = sinon.stub(); 31 | 32 | await compiler.fnFileHandler(file, currentFileInfo, handleDataAndCallCallback, callback); 33 | 34 | assert.equal(handleDataAndCallCallback.callCount, 0); 35 | assert.equal(callback.callCount, 1); 36 | assert.equal(callback.getCall(0).args.length, 1); 37 | assert.equal(callback.getCall(0).args[0].name, "TypeError"); 38 | }); 39 | it("should propagate errors via callback function (File not found)", async function() { 40 | const compiler = new Compiler({ 41 | options: { 42 | rootPaths: ["foo"] 43 | }, 44 | fileUtils: { 45 | readFile: sinon.stub().resolves(null) 46 | }, 47 | }); 48 | 49 | const file = "someFile"; 50 | const currentFileInfo = { 51 | currentDirectory: "someFolder" 52 | }; 53 | const handleDataAndCallCallback = sinon.stub(); 54 | const callback = sinon.stub(); 55 | 56 | await compiler.fnFileHandler(file, currentFileInfo, handleDataAndCallCallback, callback); 57 | 58 | assert.equal(handleDataAndCallCallback.callCount, 0); 59 | assert.equal(callback.callCount, 1); 60 | assert.equal(callback.getCall(0).args.length, 1); 61 | assert.equal(callback.getCall(0).args[0].type, "File"); 62 | assert.equal(callback.getCall(0).args[0].message, 63 | `Could not find file at path 'someFolder/someFile'` 64 | ); 65 | }); 66 | it("should propagate errors via callback function (error within handleDataAndCallCallback)", async function() { 67 | const compiler = new Compiler({ 68 | options: { 69 | rootPaths: ["foo"] 70 | }, 71 | fileUtils: { 72 | readFile: sinon.stub().resolves({ 73 | path: "", content: "" 74 | }) 75 | }, 76 | }); 77 | 78 | const file = "someFile"; 79 | const currentFileInfo = { 80 | currentDirectory: "someFolder" 81 | }; 82 | const handleDataAndCallCallback = sinon.stub().throws(new Error("Error from handleDataAndCallCallback")); 83 | const callback = sinon.stub(); 84 | 85 | await compiler.fnFileHandler(file, currentFileInfo, handleDataAndCallCallback, callback); 86 | 87 | assert.equal(handleDataAndCallCallback.callCount, 1); 88 | assert.equal(callback.callCount, 1); 89 | assert.equal(callback.getCall(0).args.length, 1); 90 | assert.equal(callback.getCall(0).args[0].message, 91 | `Error from handleDataAndCallCallback` 92 | ); 93 | }); 94 | }); 95 | -------------------------------------------------------------------------------- /test/test-css-vars.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | "use strict"; 3 | 4 | const assert = require("assert"); 5 | const readFile = require("./common/helper").readFile; 6 | 7 | // tested module 8 | const Builder = require("../").Builder; 9 | 10 | describe("css vars", function() { 11 | it("should generate the correct css variables in a simple scenario", function() { 12 | return new Builder().build({ 13 | lessInput: readFile("test/fixtures/simple/test.less"), 14 | cssVariables: true 15 | }).then(function(result) { 16 | assert.equal(result.css, readFile("test/expected/simple/test.css"), "css should be correctly generated."); 17 | assert.equal(result.cssSkeleton, readFile("test/expected/simple/test-cssvars-skeleton.css"), 18 | "skeleton css should be correctly generated."); 19 | assert.equal(result.cssVariables, readFile("test/expected/simple/test-cssvars-variables.css"), 20 | "css variables should be correctly generated."); 21 | assert.equal(result.cssVariablesSource, readFile("test/expected/simple/test-cssvars-variables.source.less"), 22 | "css variables source should be correctly generated."); 23 | }); 24 | }); 25 | 26 | it("should generate the correct css variables in a complex scenario", function() { 27 | return new Builder().build({ 28 | lessInput: readFile("test/fixtures/complex/test.less"), 29 | cssVariables: true 30 | }).then(function(result) { 31 | assert.equal(result.css, readFile("test/expected/complex/test.css"), "css should be correctly generated."); 32 | assert.equal(result.cssSkeleton, readFile("test/expected/complex/test-cssvars-skeleton.css"), 33 | "skeleton css should be correctly generated."); 34 | assert.equal(result.cssVariables, readFile("test/expected/complex/test-cssvars-variables.css"), 35 | "css variables should be correctly generated."); 36 | assert.equal(result.cssVariablesSource, 37 | readFile("test/expected/complex/test-cssvars-variables.source.less"), 38 | "css variables source should be correctly generated."); 39 | }); 40 | }); 41 | }); 42 | 43 | it("should generate the correct css variables for foo theme", function() { 44 | return new Builder().build({ 45 | lessInputPath: "my/ui/lib/themes/foo/library.source.less", 46 | rootPaths: [ 47 | "test/fixtures/libraries/lib1", 48 | "test/fixtures/libraries/lib2" 49 | ], 50 | library: { 51 | name: "my.ui.lib" 52 | }, 53 | cssVariables: true 54 | }).then(function(result) { 55 | const oVariablesExpected = { 56 | "default": { 57 | "color1": "#ffffff", 58 | "url1": "url('../base/111')", 59 | 60 | }, 61 | "scopes": { 62 | "fooContrast": { 63 | "color1": "#000000" 64 | } 65 | } 66 | }; 67 | 68 | assert.equal(result.css, readFile("test/expected/libraries/lib1/my/ui/lib/themes/foo/library.css"), 69 | "css should be correctly generated."); 70 | assert.equal(result.cssRtl, readFile("test/expected/libraries/lib1/my/ui/lib/themes/foo/library-RTL.css"), 71 | "rtl css should be correctly generated."); 72 | assert.deepEqual(result.variables, oVariablesExpected, "variables should be correctly collected."); 73 | assert.deepEqual(result.allVariables, oVariablesExpected, "allVariables should be correctly collected."); 74 | assert.equal(result.cssSkeleton, readFile("test/expected/libraries/lib1/my/ui/lib/themes/foo/library_skeleton.css"), 75 | "library_skeleton.css should be correctly generated."); 76 | assert.equal(result.cssSkeletonRtl, readFile("test/expected/libraries/lib1/my/ui/lib/themes/foo/library_skeleton-RTL.css"), 77 | "library_skeleton-RTL.css should be correctly generated."); 78 | assert.equal(result.cssVariables, readFile("test/expected/libraries/lib1/my/ui/lib/themes/foo/css_variables.css"), 79 | "css variables should be correctly generated."); 80 | assert.equal(result.cssVariablesSource, readFile("test/expected/libraries/lib1/my/ui/lib/themes/foo/css_variables.source.less"), 81 | "css variables source should be correctly generated."); 82 | }); 83 | }); 84 | -------------------------------------------------------------------------------- /test/test-custom-fs.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | "use strict"; 3 | 4 | const assert = require("assert"); 5 | const readFile = require("./common/helper").readFile; 6 | const fs = require("fs"); 7 | const path = require("path"); 8 | 9 | // tested module 10 | const Builder = require("../").Builder; 11 | 12 | describe("(custom fs) CSS Scoping of", function() { 13 | describe("comments", function() { 14 | it("should return same CSS for foo", function() { 15 | return new Builder({ 16 | fs: require("graceful-fs") 17 | }).build({ 18 | lessInputPath: "comments/themes/foo/library.source.less", 19 | rootPaths: [ 20 | "test/fixtures/libraries/scopes/comments/lib1", 21 | "test/fixtures/libraries/scopes/comments/lib2", 22 | "test/fixtures/libraries/lib1" 23 | ] 24 | }).then(function(result) { 25 | assert.equal(result.css, readFile("test/expected/libraries/scopes/comments/lib1/comments/themes/foo/library.css"), "CSS scoping should be correctly generated"); 26 | assert.equal(result.cssRtl, readFile("test/expected/libraries/scopes/comments/lib1/comments/themes/foo/library-RTL.css"), "Rtl CSS scoping should be correctly generated"); 27 | }); 28 | }); 29 | 30 | it("should return same CSS for bar", function() { 31 | let readFileCalls = 0; 32 | 33 | 34 | let statCalls = 0; 35 | 36 | return new Builder({ 37 | fs: { 38 | readFile: function(...args) { 39 | readFileCalls++; 40 | return fs.readFile(...args); 41 | }, 42 | stat: function(...args) { 43 | statCalls++; 44 | return fs.stat(...args); 45 | } 46 | } 47 | }).build({ 48 | lessInputPath: "comments/themes/bar/library.source.less", 49 | rootPaths: [ 50 | "test/fixtures/libraries/scopes/comments/lib1", 51 | "test/fixtures/libraries/scopes/comments/lib2", 52 | "test/fixtures/libraries/lib1", 53 | "test/fixtures/libraries/lib2" 54 | ] 55 | }).then(function(result) { 56 | assert.equal(result.css, readFile("test/expected/libraries/scopes/comments/lib2/comments/themes/bar/library.css"), "CSS scoping should be correctly generated"); 57 | assert.equal(result.cssRtl, readFile("test/expected/libraries/scopes/comments/lib2/comments/themes/bar/library-RTL.css"), "Rtl CSS scoping should be correctly generated"); 58 | assert.equal(readFileCalls, 3, "fs.readFile should have been called 3 times"); 59 | assert.equal(statCalls, 19, "fs.stat should have been called 19 times"); 60 | }); 61 | }); 62 | 63 | 64 | it("should return same CSS for bar with absolute import paths", function() { 65 | const readFileCalls = []; 66 | 67 | 68 | let statCalls = 0; 69 | 70 | return new Builder({ 71 | fs: { 72 | readFile: function(...args) { 73 | readFileCalls.push(args[0]); 74 | return fs.readFile(...args); 75 | }, 76 | stat: function(...args) { 77 | statCalls++; 78 | return fs.stat(...args); 79 | } 80 | } 81 | }).build({ 82 | lessInputPath: "lib3/comments/themes/bar/library.source.less", 83 | rootPaths: [ 84 | "test/fixtures/libraries/scopes/comments" 85 | ] 86 | }).then(function(result) { 87 | assert.equal(result.css, readFile("test/expected/libraries/scopes/comments/lib3/comments/themes/bar/library.css"), "CSS scoping should be correctly generated"); 88 | assert.equal(result.cssRtl, readFile("test/expected/libraries/scopes/comments/lib3/comments/themes/bar/library-RTL.css"), "Rtl CSS scoping should be correctly generated"); 89 | 90 | const basePath = path.join("test/fixtures/libraries/scopes/comments/lib3/comments", "themes"); 91 | 92 | assert.deepEqual(readFileCalls, [ 93 | path.join(basePath, "bar/library.source.less"), 94 | path.join(basePath, "other/sub/my.less"), 95 | path.join(basePath, "bar/my2.less"), 96 | path.join(basePath, "my3.less") 97 | ], "fs.readFile should have been called with import paths"); 98 | assert.equal(statCalls, 10, "fs.stat should have been called 19 times"); 99 | }); 100 | }); 101 | }); 102 | }); 103 | -------------------------------------------------------------------------------- /test/test-diff.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | "use strict"; 3 | 4 | const assert = require("assert"); 5 | const css = require("@adobe/css-tools"); 6 | const fs = require("fs"); 7 | const path = require("path"); 8 | 9 | // tested module 10 | const diff = require("../lib/diff"); 11 | 12 | const options = { 13 | encoding: "utf8" 14 | }; 15 | 16 | const cssPath = "test/fixtures/diff/css"; 17 | 18 | describe("Diff algorithm", function() { 19 | it("should create a diff with moved comment block in compare and additional rule in compare", function() { 20 | const compareCSS = fs.readFileSync(path.join(cssPath, "library1/compare.css"), options); 21 | const baseCSS = fs.readFileSync(path.join(cssPath, "library1/base.css"), options); 22 | 23 | // Create diff object between embeddedCompare and embedded 24 | const oBase = css.parse(baseCSS); 25 | const oEmbedded = css.parse(compareCSS); 26 | 27 | const oResult = diff(oBase, oEmbedded); 28 | 29 | assert.deepStrictEqual(oResult.diff.stylesheet.rules.map(convertRuleToComparableString), [ 30 | { 31 | "type": "rule", 32 | "value": "a" 33 | }, 34 | { 35 | "type": "rule", 36 | "value": "b" 37 | }, 38 | { 39 | "type": "comment", 40 | "value": " mine " 41 | }, 42 | { 43 | "type": "rule", 44 | "value": "b.test" 45 | } 46 | ]); 47 | assert.deepStrictEqual(oResult.stack.stylesheet.rules.map(convertRuleToComparableString), [ 48 | { 49 | "type": "rule", 50 | "value": "html" 51 | }, 52 | { 53 | "type": "comment", 54 | "value": " mine2 " 55 | }, 56 | { 57 | "type": "comment", 58 | "value": " single " 59 | } 60 | ]); 61 | }); 62 | it("should create a diff with more rules in base than in compare", function() { 63 | const compareCSS = fs.readFileSync(path.join(cssPath, "library2/compare.css"), options); 64 | const baseCSS = fs.readFileSync(path.join(cssPath, "library2/base.css"), options); 65 | 66 | // Create diff object between embeddedCompare and embedded 67 | const oBase = css.parse(baseCSS); 68 | const oEmbedded = css.parse(compareCSS); 69 | 70 | const oResult = diff(oBase, oEmbedded); 71 | 72 | assert.deepStrictEqual(oResult.diff.stylesheet.rules.map(convertRuleToComparableString), [ 73 | { 74 | "type": "rule", 75 | "value": "a" 76 | }, 77 | { 78 | "type": "rule", 79 | "value": "b" 80 | } 81 | ]); 82 | assert.deepStrictEqual(oResult.stack.stylesheet.rules.map(convertRuleToComparableString), []); 83 | }); 84 | }); 85 | 86 | 87 | const convertRuleToComparableString = function(rule) { 88 | if (rule.type === "comment") { 89 | return { 90 | type: rule.type, 91 | value: rule.comment 92 | }; 93 | } else if (rule.type === "rule") { 94 | return { 95 | type: rule.type, 96 | value: rule.selectors.join(",") 97 | }; 98 | } 99 | return { 100 | type: rule.type, 101 | value: rule.toString() 102 | }; 103 | }; 104 | -------------------------------------------------------------------------------- /test/test-perf-workaround.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | "use strict"; 3 | 4 | const assert = require("assert"); 5 | const path = require("path"); 6 | const readFile = require("./common/helper").readFile; 7 | 8 | // tested module 9 | const Builder = require("../").Builder; 10 | 11 | describe("performance workaround", function() { 12 | it("should run with patched String prototype", function() { 13 | function customGetter() { 14 | return "customGetter"; 15 | } 16 | String.prototype.__defineGetter__("customGetter", customGetter); 17 | 18 | function customProp() { 19 | return "customProp"; 20 | } 21 | // eslint-disable-next-line no-extend-native 22 | String.prototype.customProp = customProp; 23 | 24 | return new Builder().build({ 25 | lessInputPath: "my/ui/lib/themes/foo/library.source.less", 26 | rootPaths: [ 27 | "test/fixtures/libraries/lib1", 28 | "test/fixtures/libraries/lib2" 29 | ], 30 | library: { 31 | name: "my.ui.lib" 32 | } 33 | }).then(function(result) { 34 | const oVariablesExpected = { 35 | "default": { 36 | "color1": "#ffffff", 37 | "url1": "url('../base/111')", 38 | }, 39 | "scopes": { 40 | "fooContrast": { 41 | "color1": "#000000" 42 | } 43 | } 44 | }; 45 | 46 | assert.equal(result.css, readFile("test/expected/libraries/lib1/my/ui/lib/themes/foo/library.css"), "css should be correctly generated."); 47 | assert.equal(result.cssRtl, readFile("test/expected/libraries/lib1/my/ui/lib/themes/foo/library-RTL.css"), "rtl css should be correctly generated."); 48 | assert.deepEqual(result.variables, oVariablesExpected, "variables should be correctly collected."); 49 | assert.deepEqual(result.imports, [ 50 | path.join("test", "fixtures", "libraries", "lib1", "my", "ui", "lib", "themes", "foo", "library.source.less"), 51 | path.join("test", "fixtures", "libraries", "lib1", "my", "ui", "lib", "themes", "base", "library.source.less"), 52 | path.join("test", "fixtures", "libraries", "lib1", "my", "ui", "lib", "themes", "base", "global.less"), 53 | path.join("test", "fixtures", "libraries", "lib1", "my", "ui", "lib", "themes", "foo", "global.less"), 54 | path.join("test", "fixtures", "libraries", "lib2", "my", "ui", "lib", "themes", "bar", "library.source.less"), 55 | path.join("test", "fixtures", "libraries", "lib2", "my", "ui", "lib", "themes", "bar", "global.less"), 56 | path.join("test", "fixtures", "libraries", "lib1", "sap", "ui", "core", "themes", "foo", ".theming") 57 | ], "import list should be correct."); 58 | 59 | assert.strictEqual(String.prototype.__lookupGetter__("customGetter"), customGetter, "Custom getter should again be set on String prototype."); 60 | assert.strictEqual(String.prototype.customProp, customProp, "Custom property should again be set on String prototype."); 61 | }); 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /test/test-variable-collector.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | "use strict"; 3 | 4 | const assert = require("assert"); 5 | const VariableCollector = require("../lib/plugin/variable-collector"); 6 | 7 | describe("VariableCollector", function() { 8 | it("should return relevant variables when calling 'getVariables'", function() { 9 | const env = {}; 10 | const variableCollector = new VariableCollector(env); 11 | 12 | variableCollector.mVariables = { 13 | "color1": { 14 | "value": "#ffffff", 15 | "filename": "my/ui/lib/themes/foo/global.less", 16 | "rootFilename": "my/other/ui/lib/themes/foo/library.source.less" 17 | }, 18 | "_my_other_ui_lib_MyControl_color1": { 19 | "value": "#ffffff", 20 | "filename": "my/other/ui/lib/themes/foo/MyControl.less", 21 | "rootFilename": "my/other/ui/lib/themes/foo/library.source.less" 22 | }, 23 | "_my_other_ui_lib_MyOtherControl_color1": { 24 | "value": "#ffffff", 25 | "filename": "my/other/ui/lib/themes/base/sub-directory/MyOtherControl.less", 26 | "rootFilename": "my/other/ui/lib/themes/foo/library.source.less" 27 | }, 28 | "_my_other_ui_lib_MyControl_color2": { 29 | "value": "#008000", 30 | "filename": "my/other/ui/lib/themes/foo/MyControl.less", 31 | "rootFilename": "my/other/ui/lib/themes/foo/library.source.less" 32 | } 33 | }; 34 | 35 | const aImports = [ 36 | "my/ui/lib/themes/foo/global.less", 37 | "my/other/ui/lib/themes/foo/MyControl.less", 38 | "my/other/ui/lib/themes/base/library.source.less", 39 | "my/ui/lib/themes/base/global.less", 40 | "my/other/ui/lib/themes/base/MyControl.less", 41 | "my/other/ui/lib/themes/base/sub-directory/MyOtherControl.less" 42 | ]; 43 | 44 | const variables = variableCollector.getVariables(aImports); 45 | 46 | assert.deepEqual(variables, { 47 | "_my_other_ui_lib_MyControl_color1": "#ffffff", 48 | "_my_other_ui_lib_MyOtherControl_color1": "#ffffff", 49 | "_my_other_ui_lib_MyControl_color2": "#008000" 50 | }, "relevant variables should be returned"); 51 | }); 52 | it("should return relevant variables when calling 'getVariables' (win32 paths)", function() { 53 | const env = {}; 54 | const variableCollector = new VariableCollector(env); 55 | 56 | variableCollector.mVariables = { 57 | "color1": { 58 | "value": "#ffffff", 59 | "filename": "my\\ui\\lib\\themes\\foo\\global.less", 60 | "rootFilename": "my\\other\\ui\\lib\\themes\\foo\\library.source.less" 61 | }, 62 | "_my_other_ui_lib_MyControl_color1": { 63 | "value": "#ffffff", 64 | "filename": "my\\other\\ui\\lib\\themes\\foo\\MyControl.less", 65 | "rootFilename": "my\\other\\ui\\lib\\themes\\foo\\library.source.less" 66 | }, 67 | "_my_other_ui_lib_MyOtherControl_color1": { 68 | "value": "#ffffff", 69 | "filename": "my\\other\\ui\\lib\\themes\\base\\sub-directory\\MyOtherControl.less", 70 | "rootFilename": "my\\other\\ui\\lib\\themes\\foo\\library.source.less" 71 | }, 72 | "_my_other_ui_lib_MyControl_color2": { 73 | "value": "#008000", 74 | "filename": "my\\other\\ui\\lib\\themes\\foo\\MyControl.less", 75 | "rootFilename": "my\\other\\ui\\lib\\themes\\foo\\library.source.less" 76 | } 77 | }; 78 | 79 | const aImports = [ 80 | "my\\ui\\lib\\themes\\foo\\global.less", 81 | "my\\other\\ui\\lib\\themes\\foo\\MyControl.less", 82 | "my\\other\\ui\\lib\\themes\\base\\library.source.less", 83 | "my\\ui\\lib\\themes\\base\\global.less", 84 | "my\\other\\ui\\lib\\themes\\base\\MyControl.less", 85 | "my\\other\\ui\\lib\\themes\\base\\sub-directory\\MyOtherControl.less" 86 | ]; 87 | 88 | const variables = variableCollector.getVariables(aImports); 89 | 90 | assert.deepEqual(variables, { 91 | "_my_other_ui_lib_MyControl_color1": "#ffffff", 92 | "_my_other_ui_lib_MyOtherControl_color1": "#ffffff", 93 | "_my_other_ui_lib_MyControl_color2": "#008000" 94 | }, "relevant variables should be returned"); 95 | }); 96 | it("should return variables when calling 'getVariables' (filename = rootFilename, no imports)", function() { 97 | const env = {}; 98 | const variableCollector = new VariableCollector(env); 99 | 100 | variableCollector.mVariables = { 101 | "color1": { 102 | "value": "#ffffff", 103 | "filename": "something", 104 | "rootFilename": "something" 105 | } 106 | }; 107 | const aImports = []; 108 | 109 | const variables = variableCollector.getVariables(aImports); 110 | 111 | assert.deepEqual(variables, { 112 | "color1": "#ffffff" 113 | }, "relevant variables should be returned"); 114 | }); 115 | }); 116 | --------------------------------------------------------------------------------