@tarojs/rn-style-transformer 3.3.6 → 3.3.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,6 +3,7 @@ import pxtransform from 'postcss-pxtransform'
3
3
  import postcssImport from 'postcss-import'
4
4
  import { recursiveMerge } from '@tarojs/helper'
5
5
  import { resolveStyle } from '../utils'
6
+ import reporterSkip from '../utils/reporterSkip'
6
7
  import stylelintConfig from '../config/rn-stylelint.json'
7
8
 
8
9
  export interface Config {
@@ -11,7 +12,7 @@ export interface Config {
11
12
  pxtransform: {
12
13
  enable: boolean;
13
14
  config: any;
14
- },
15
+ };
15
16
  }
16
17
 
17
18
  const defaultPxtransformOption: {
@@ -23,11 +24,13 @@ const defaultPxtransformOption: {
23
24
  }
24
25
  }
25
26
 
26
- export function getPostcssPlugins ({
27
+ export function makePostcssPlugins ({
28
+ filename,
27
29
  designWidth,
28
30
  deviceRatio,
29
31
  postcssConfig,
30
- transformOptions
32
+ transformOptions,
33
+ additionalData
31
34
  }) {
32
35
  if (designWidth) {
33
36
  defaultPxtransformOption.config.designWidth = designWidth
@@ -59,8 +62,11 @@ export function getPostcssPlugins ({
59
62
  plugins.push(pxtransform(pxtransformOption.config))
60
63
  }
61
64
 
65
+ const skipRows = additionalData ? additionalData.split('\n').length : 0
66
+
62
67
  plugins.push(
63
68
  require('stylelint')(stylelintConfig),
69
+ reporterSkip({ skipRows, filename }),
64
70
  require('postcss-reporter')({ clearReportedMessages: true })
65
71
  )
66
72
 
@@ -71,7 +77,6 @@ export default function transform (src: string, filename: string, { options, plu
71
77
  return postcss(plugins)
72
78
  .process(src, { from: filename, ...options })
73
79
  .then(result => {
74
- const css = result.css
75
- return css
80
+ return result
76
81
  })
77
82
  }
@@ -1,17 +1,18 @@
1
1
  import fs from 'fs'
2
2
  import path from 'path'
3
3
  import sass, { Options } from 'sass'
4
- import { insertBefore, resolveStyle } from '../utils'
5
- import { TransformOptions } from '../types'
4
+ import { insertAfter, insertBefore, resolveStyle, getAdditionalData } from '../utils'
5
+ import { TransformOptions, RenderResult, RenderAdditionalResult } from '../types'
6
6
 
7
7
  // https://github.com/sass/node-sass#options
8
8
  export interface Config {
9
+ sass?: SassGlobalConfig
9
10
  alias?: Record<string, string>
10
11
  options?: Options
11
- additionalData?: string | ((string) => string)
12
+ additionalData?: string | ((key: string) => string)
12
13
  }
13
14
 
14
- export interface SassExternalConfig {
15
+ export interface SassGlobalConfig {
15
16
  resource?: string | string[];
16
17
  projectDirectory?: string;
17
18
  data?: string;
@@ -28,16 +29,10 @@ function makeURL (resource: string, rootDir: string) {
28
29
  function makeImportStatement (filePath: string, resource: string, rootDir: string) {
29
30
  const url = makeURL(resource, rootDir)
30
31
  const relativePath = path.relative(filePath, url).replace(/\\/g, '/') // fix window path error
31
- return `@import '${relativePath}'`
32
+ return `@import './${relativePath}'`
32
33
  }
33
34
 
34
- /**
35
- * 处理 additional 配置,比如 config.sass 配置
36
- * @param src
37
- * @param filename
38
- * @returns
39
- */
40
- export function processByExternal (src, filename, config: SassExternalConfig) {
35
+ function getGlobalResource (filename: string, config: SassGlobalConfig) {
41
36
  let resource = ''
42
37
  const projectDirectory = config.projectDirectory || process.cwd()
43
38
  const filePath = path.dirname(path.resolve(projectDirectory, filename))
@@ -48,8 +43,18 @@ export function processByExternal (src, filename, config: SassExternalConfig) {
48
43
  const resources = config.resource?.map(source => makeImportStatement(filePath, source, projectDirectory)) || []
49
44
  resource = resources.join(';\n')
50
45
  }
51
- const combineSource = insertBefore(config?.data, resource)
52
- return insertBefore(src, combineSource)
46
+ // https://taro-docs.jd.com/taro/docs/config-detail/#sassdata, data 覆盖 resurce 配置
47
+ return insertAfter(resource, config?.data)
48
+ }
49
+
50
+ function combineResource (src: string, filename: string, config: Config) {
51
+ // sass config
52
+ const globalResource = getGlobalResource(filename, config.sass)
53
+
54
+ // sass tranform config
55
+ const additionalData = getAdditionalData(src, config.additionalData)
56
+
57
+ return insertAfter(globalResource, additionalData)
53
58
  }
54
59
 
55
60
  function renderToCSS (src, filename, options, transformOptions) {
@@ -98,8 +103,7 @@ function renderToCSS (src, filename, options, transformOptions) {
98
103
  if (err) {
99
104
  reject(err)
100
105
  } else {
101
- const css = result.css.toString()
102
- resolve(css)
106
+ resolve(result)
103
107
  }
104
108
  })
105
109
  })
@@ -111,21 +115,25 @@ export default function transform (
111
115
  config: Config,
112
116
  transformOptions: TransformOptions
113
117
  ) {
114
- let data = src
118
+ const additionalData = combineResource(src, filename, config)
119
+ let data = insertBefore(src, additionalData)
115
120
 
116
121
  if (!data) {
117
122
  data = `\n${data}` // fix empty file error. reference https://github.com/sass/node-sass/blob/91c40a0bf0a3923ab9f91b82dcd479c25486235a/lib/index.js#L430
118
123
  }
119
124
 
120
- if (typeof config.additionalData !== 'undefined') {
121
- data =
122
- typeof config.additionalData === 'function'
123
- ? `${config.additionalData(data)}`
124
- : `${config.additionalData}\n${data}`
125
- }
126
-
127
- return renderToCSS(data, filename, { ...config.options, alias: config.alias }, transformOptions)
128
- .then((css: string) => {
129
- return css
130
- })
125
+ return renderToCSS(
126
+ data,
127
+ filename,
128
+ {
129
+ file: filename,
130
+ outFile: `${filename}.map`,
131
+ sourceMap: true, // If no outFile is set, sourceMap parameter is ignored.
132
+ alias: config.alias,
133
+ ...config.options
134
+ },
135
+ transformOptions
136
+ ).then((result: RenderResult) => {
137
+ return { ...result, additionalData } as RenderAdditionalResult
138
+ })
131
139
  }
@@ -1,4 +1,6 @@
1
1
  import stylus from 'stylus'
2
+ import { RenderResult, RenderAdditionalResult } from '../types'
3
+ import { getAdditionalData, insertBefore } from '../utils'
2
4
 
3
5
  class Evaluator { }
4
6
 
@@ -99,7 +101,7 @@ interface RenderOptions {
99
101
  export interface Config {
100
102
  alias?: Record<string, string>
101
103
  options: RenderOptions
102
- additionalData?: string | ((string) => string)
104
+ additionalData?: string | ((key: string) => string)
103
105
  }
104
106
 
105
107
  export const defaultOptions = {
@@ -118,6 +120,15 @@ function renderToCSS (src, filename, options = {} as RenderOptions) {
118
120
  const stylusOptions = { filename, ...options }
119
121
  const styl = stylus(src, stylusOptions)
120
122
 
123
+ styl.set(
124
+ 'sourcemap',
125
+ {
126
+ comment: true,
127
+ sourceRoot: '.', // stylusOptions.dest
128
+ basePath: '.'
129
+ }
130
+ )
131
+
121
132
  // include regular CSS on @import
122
133
  if (stylusOptions.includeCSS) {
123
134
  styl.set('include css', true)
@@ -191,23 +202,24 @@ function renderToCSS (src, filename, options = {} as RenderOptions) {
191
202
  if (err) {
192
203
  reject(err)
193
204
  } else {
194
- resolve(css)
205
+ resolve({
206
+ css,
207
+ map: styl.sourcemap
208
+ })
195
209
  }
196
210
  })
197
211
  })
198
212
  }
199
213
 
200
214
  export default function transform (src: string, filename: string, config: Config) {
201
- let data = src
202
-
203
- if (typeof config.additionalData !== 'undefined') {
204
- data =
205
- typeof config.additionalData === 'function'
206
- ? `${config.additionalData(data)}`
207
- : `${config.additionalData}\n${data}`
208
- }
209
- return renderToCSS(data, filename, config.options)
210
- .then((css: string) => {
211
- return css
212
- })
215
+ const additionalData = getAdditionalData(src, config.additionalData)
216
+ const data = insertBefore(src, additionalData)
217
+
218
+ return renderToCSS(
219
+ data,
220
+ filename,
221
+ config.options
222
+ ).then((result: RenderResult) => {
223
+ return { ...result, additionalData } as RenderAdditionalResult
224
+ })
213
225
  }
@@ -8,7 +8,7 @@ export interface TransformOptions {
8
8
  customTransformOptions: any
9
9
  }
10
10
 
11
- export enum LogLevelEnum {
11
+ export const enum LogLevelEnum {
12
12
  ERROR = 'error',
13
13
  WARNING = 'warning',
14
14
  }
@@ -21,3 +21,12 @@ export interface ResolveStyleOptions {
21
21
  defaultExt?: string
22
22
  alias?: Record<string, string>
23
23
  }
24
+
25
+ export interface RenderResult {
26
+ css: string | Buffer
27
+ map?: string | Buffer
28
+ }
29
+
30
+ export interface RenderAdditionalResult extends RenderResult {
31
+ additionalData: string
32
+ }
@@ -2,31 +2,33 @@ import fs from 'fs'
2
2
  import path from 'path'
3
3
  import { printLog, processTypeEnum } from '@tarojs/helper'
4
4
  import { ResolveStyleOptions, LogLevelEnum } from '../types'
5
+ import resolve from 'resolve'
6
+ import nodeModulesPaths from 'resolve/lib/node-modules-paths'
5
7
 
6
- export function insertBefore (source: string, target: string) {
7
- if (!source && !target) {
8
+ export function insertBefore (source: string, additional: string) {
9
+ if (!source && !additional) {
8
10
  return ''
9
11
  }
10
12
  if (!source) {
11
- return target
13
+ return additional
12
14
  }
13
- if (!target) {
15
+ if (!additional) {
14
16
  return source
15
17
  }
16
- return target + ';\n' + source
18
+ return additional + ';\n' + source
17
19
  }
18
20
 
19
- export function insertAfter (source: string, target: string) {
20
- if (!source && !target) {
21
+ export function insertAfter (source: string, additional: string) {
22
+ if (!source && !additional) {
21
23
  return ''
22
24
  }
23
25
  if (!source) {
24
- return target
26
+ return additional
25
27
  }
26
- if (!target) {
28
+ if (!additional) {
27
29
  return source
28
30
  }
29
- return source + ';\n' + target
31
+ return source + ';\n' + additional
30
32
  }
31
33
 
32
34
  // Iterate through the include paths and extensions to find the file variant
@@ -62,6 +64,7 @@ export function resolveStyle (id: string, opts: ResolveStyleOptions) {
62
64
  defaultExt = '',
63
65
  logLevel = LogLevelEnum.ERROR
64
66
  } = opts
67
+ id = id.trim()
65
68
  Object.keys(alias).forEach(key => {
66
69
  if (id.startsWith(key)) {
67
70
  id = id.replace(key, alias[key])
@@ -72,19 +75,36 @@ export function resolveStyle (id: string, opts: ResolveStyleOptions) {
72
75
  const incPaths = [path.resolve(basedir, dir)].concat(paths)
73
76
  const ext = idExt || defaultExt
74
77
 
75
- const exts = [
78
+ const extensions = [
76
79
  // add the platform specific extension, first in the array to take precedence
77
80
  platform === 'android' ? '.android' + ext : '.ios' + ext,
78
81
  '.rn' + ext,
79
82
  ext
80
83
  ]
81
84
 
82
- const file = findVariant(name, exts, incPaths)
85
+ let file: string
86
+ let isNodeModulesPath = false
87
+ try {
88
+ if ((/^(?:\.\.?(?:\/|$)|\/|([A-Za-z]:)?[/\\])/).test(id)) {
89
+ file = findVariant(name, extensions, incPaths)
90
+ } else {
91
+ // lookup node_modules file
92
+ isNodeModulesPath = true
93
+ // like `@import 'taro-ui/dist/base.css';` or `@import '~taro-ui/dist/base.css';`
94
+ file = resolve.sync(path.join(dir, name).replace(/^~/, ''), { basedir, extensions })
95
+ }
96
+ } catch (error) {
97
+ }
98
+
83
99
  if (!file) {
100
+ let includePaths = incPaths
101
+ if (isNodeModulesPath) {
102
+ includePaths = nodeModulesPaths(basedir, { extensions }, id)
103
+ }
84
104
  const levelMessage = `
85
105
  样式文件没有找到,请检查文件路径: ${id}
86
106
  在 [
87
- ${incPaths.join(',\n ')}
107
+ ${includePaths.join(',\n ')}
88
108
  ]
89
109
  `
90
110
  if (logLevel === LogLevelEnum.ERROR) {
@@ -99,4 +119,83 @@ export function resolveStyle (id: string, opts: ResolveStyleOptions) {
99
119
  return file
100
120
  }
101
121
 
122
+ // copy from https://github.com/webpack-contrib/css-loader/blob/master/src/utils.js
123
+ const IS_NATIVE_WIN32_PATH = /^[a-z]:[/\\]|^\\\\/i
124
+ const ABSOLUTE_SCHEME = /^[a-z0-9+\-.]+:/i
125
+
126
+ export function normalizePath (file) {
127
+ return path.sep === '\\' ? file.replace(/\\/g, '/') : file
128
+ }
129
+
130
+ function getURLType (source) {
131
+ if (source[0] === '/') {
132
+ if (source[1] === '/') {
133
+ return 'scheme-relative'
134
+ }
135
+
136
+ return 'path-absolute'
137
+ }
138
+
139
+ if (IS_NATIVE_WIN32_PATH.test(source)) {
140
+ return 'path-absolute'
141
+ }
142
+
143
+ return ABSOLUTE_SCHEME.test(source) ? 'absolute' : 'path-relative'
144
+ }
145
+
146
+ export function normalizeSourceMap (map, resourcePath) {
147
+ let newMap = map
148
+
149
+ // Some loader emit source map as string
150
+ // Strip any JSON XSSI avoidance prefix from the string (as documented in the source maps specification), and then parse the string as JSON.
151
+ if (typeof newMap === 'string') {
152
+ newMap = JSON.parse(newMap)
153
+ }
154
+
155
+ delete newMap.file
156
+
157
+ const { sourceRoot } = newMap
158
+
159
+ delete newMap.sourceRoot
160
+
161
+ if (newMap.sources) {
162
+ // Source maps should use forward slash because it is URLs (https://github.com/mozilla/source-map/issues/91)
163
+ // We should normalize path because previous loaders like `sass-loader` using backslash when generate source map
164
+ newMap.sources = newMap.sources.map((source) => {
165
+ // Non-standard syntax from `postcss`
166
+ if (source.indexOf('<') === 0) {
167
+ return source
168
+ }
169
+
170
+ const sourceType = getURLType(source)
171
+
172
+ // Do no touch `scheme-relative` and `absolute` URLs
173
+ if (sourceType === 'path-relative' || sourceType === 'path-absolute') {
174
+ const absoluteSource =
175
+ sourceType === 'path-relative' && sourceRoot
176
+ ? path.resolve(sourceRoot, normalizePath(source))
177
+ : normalizePath(source)
178
+
179
+ return path.relative(path.dirname(resourcePath), absoluteSource)
180
+ }
181
+
182
+ return source
183
+ })
184
+ }
185
+
186
+ return newMap
187
+ }
188
+ // copy end
189
+
190
+ export function getAdditionalData (data: string, config: string | ((key: string) => string)) {
191
+ let additionalData = ''
192
+ if (typeof config !== 'undefined') {
193
+ additionalData =
194
+ typeof config === 'function'
195
+ ? `${config(data)}`
196
+ : config
197
+ }
198
+ return additionalData
199
+ }
200
+
102
201
  export default {}
@@ -1,47 +1,46 @@
1
- import path from 'path'
1
+ import less from 'less'
2
2
  import { resolveStyle } from './index'
3
- import { LogLevelEnum } from '../types'
4
3
 
5
- class Importer {
4
+ class Importer extends less.FileManager {
5
+ platform: 'android' | 'ios'
6
+ alias: Record<string, string> = {}
7
+
6
8
  constructor (opt) {
9
+ super()
7
10
  this.platform = opt.platform
8
11
  this.alias = opt.alias
9
12
  }
10
13
 
11
- platform: 'android' | 'ios'
12
-
13
- alias: Record<string, string> = {}
14
-
15
- process (src: string, options: Less.PreProcessorExtraInfo) {
16
- const { fileInfo } = options
17
- const { filename, currentDirectory: basedir } = fileInfo
14
+ supports () {
15
+ return true
16
+ }
18
17
 
19
- if (!basedir) {
20
- return src
21
- }
18
+ supportsSync () {
19
+ return false
20
+ }
22
21
 
22
+ async loadFile (
23
+ filename: string,
24
+ currentDirectory: string,
25
+ options: any,
26
+ environment: any
27
+ ) {
23
28
  const resolveOpts = {
24
- basedir,
29
+ basedir: currentDirectory,
25
30
  alias: this.alias,
26
31
  platform: this.platform,
27
- logLevel: LogLevelEnum.WARNING,
28
- defaultExt: path.extname(filename)
32
+ defaultExt: '.less'
29
33
  }
34
+ const rewriteFilename = resolveStyle(filename, resolveOpts)
30
35
 
31
- // 解析 @import "a.less" 字符串里面的内容
32
- src = src.replace(/@import\s+['"]([^'|"]*)['"]/gi, (_, id) => {
33
- const relativePath = path.relative(basedir, resolveStyle(id.trim(), resolveOpts)).replace(/\\/g, '/')
34
- return `@import '${relativePath}';`
35
- })
36
-
37
- return src
36
+ return super.loadFile(rewriteFilename, currentDirectory, options, environment)
38
37
  }
39
38
  }
40
39
 
41
40
  function makeLessImport (options) {
42
41
  return {
43
42
  install: (_, pluginManager) => {
44
- pluginManager.addPreProcessor(new Importer(options))
43
+ pluginManager.addFileManager(new Importer(options))
45
44
  },
46
45
  minVersion: [2, 7, 1]
47
46
  }
@@ -0,0 +1,24 @@
1
+ interface Location {
2
+ line: number
3
+ column: number
4
+ file: string
5
+ }
6
+
7
+ export default function reporterSkip ({ skipRows, filename }) {
8
+ // @ts-ignore
9
+ return (css, result) => {
10
+ result.messages?.forEach((message: any) => {
11
+ const { line, column, node } = message
12
+
13
+ const messageInput = node?.source?.input
14
+ if (messageInput) {
15
+ const originLocation: Location = messageInput.origin(line, column)
16
+ // 如果是原始引入的样式文件,则对拼接的代码(addionalData)函数做减法
17
+ if (originLocation && originLocation.file.includes(filename)) {
18
+ // force modify line,then source map cannot read origin column, value is 0.
19
+ message.line -= skipRows
20
+ }
21
+ }
22
+ })
23
+ }
24
+ }